From 5a012e5cc1a11771df499f420dba720408a3854e Mon Sep 17 00:00:00 2001 From: MithunR Date: Thu, 29 Apr 2021 05:24:19 -0700 Subject: [PATCH 01/65] Extend range window queries to non-timestamp order-by columns (#7866) (Redux of #7675, which was for `branch-0.19`, and included support to scale duration scalars to the orderby column's resolution.) This change introduces a `grouped_range_rolling_window()` function, to generalize the functionality available through `grouped_time_range_rolling_window()`. ### Prelude Recall that `grouped_time_range_rolling_window()` applies an aggregation over a dynamic window of rows. The width of the window is determined based on a timestamp column, and the relative preceding/following offset specified in days. E.g. Consider this dataset: ``` [ // user, sales_amt, YYYYMMDD (date) { "user1", 10, 20200101 }, { "user2", 20, 20200101 }, { "user1", 20, 20200102 }, { "user1", 10, 20200103 }, { "user2", 30, 20200101 }, { "user2", 80, 20200102 }, { "user1", 50, 20200107 }, { "user1", 60, 20200107 }, { "user2", 40, 20200104 } ] ``` Grouping by `user_id`, and ordering by `date` yields the following `sales_amt` vector (with 2 groups, one for each distinct `user_id`): ``` Date :(202001-) [ 01, 02, 03, 07, 07, 01, 01, 02, 04 ] Input: [ 10, 20, 10, 50, 60, 20, 30, 80, 40 ] <-------user1-------->|<---------user2---------> ``` The `SUM` aggregation applied over a window of `(1 DAY PRECEDING, 1 DAY FOLLOWING)` with a minimum of 1 period yields the following result column: ``` Results: [ 30, 40, 30, 110, 110, 130, 130, 130, 40 ] ``` ### What's new in this commit `grouped_range_rolling_window()` extends the erstwhile range-window functionality in two ways: 1. The order-by column (`date`) is **not restricted to timestamps**. This may also be a sorted integral column. The preceding/following offsets will then need to be specified as scalars of the same type as the order-by column. E.g. ``` [ // user, sales_amt, seconds_since_snap { "user1", 10, 86451 }, { "user2", 20, 86735 }, { "user1", 20, 89162 }, { "user1", 10, 92152 }, ...] ``` 2. If the order-by column is indeed a timestamp, the **preceding/following offsets may be duration scalars**, whose precision must match that of the timestamp order-by column. E.g. If `orderby_column.type() == TIMESTAMP_SECONDS`, `preceding.type()` may only be `DURATION_SECONDS`. Analogous to `window_bounds`, a new `range_window_bounds` class has been introduced to specify bounds for range window functions: 1. Supports scalar values for preceding/following 2. Supports `UNBOUNDED` window widths `range_window_bounds` currently supports only integral and duration scalars. Correspondingly, the `orderby_column` can only be integral and timestamp columns. Support for floats and fixed_point types may be added at a later date. The existing `grouped_time_range_rolling_window()` function now delegates to the same backend as `grouped_range_rolling_window()`. Authors: - MithunR (https://github.com/mythrocks) Approvers: - Mike Wendt (https://github.com/mike-wendt) - Robert Maynard (https://github.com/robertmaynard) - https://github.com/nvdbaranec - Christopher Harris (https://github.com/cwharris) - Vukasin Milovanovic (https://github.com/vuule) - Jordan Jacobelli (https://github.com/Ethyling) URL: https://github.com/rapidsai/cudf/pull/7866 --- conda/recipes/libcudf/meta.yaml | 1 + cpp/CMakeLists.txt | 1 + cpp/include/cudf/rolling.hpp | 163 +++- .../cudf/rolling/range_window_bounds.hpp | 77 ++ cpp/src/rolling/grouped_rolling.cu | 718 +++++++++++------- cpp/src/rolling/range_window_bounds.cpp | 78 ++ .../rolling/range_window_bounds_detail.hpp | 134 ++++ cpp/tests/CMakeLists.txt | 5 +- .../rolling/range_rolling_window_test.cpp | 603 +++++++++++++++ .../rolling/range_window_bounds_test.cpp | 155 ++++ 10 files changed, 1652 insertions(+), 283 deletions(-) create mode 100644 cpp/include/cudf/rolling/range_window_bounds.hpp create mode 100644 cpp/src/rolling/range_window_bounds.cpp create mode 100644 cpp/src/rolling/range_window_bounds_detail.hpp create mode 100644 cpp/tests/rolling/range_rolling_window_test.cpp create mode 100644 cpp/tests/rolling/range_window_bounds_test.cpp diff --git a/conda/recipes/libcudf/meta.yaml b/conda/recipes/libcudf/meta.yaml index 75955428eab..4386a62e32a 100644 --- a/conda/recipes/libcudf/meta.yaml +++ b/conda/recipes/libcudf/meta.yaml @@ -152,6 +152,7 @@ test: - test -f $PREFIX/include/cudf/replace.hpp - test -f $PREFIX/include/cudf/reshape.hpp - test -f $PREFIX/include/cudf/rolling.hpp + - test -f $PREFIX/include/cudf/rolling/range_window_bounds.hpp - test -f $PREFIX/include/cudf/round.hpp - test -f $PREFIX/include/cudf/scalar/scalar_factories.hpp - test -f $PREFIX/include/cudf/scalar/scalar.hpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 8431e23078b..82c80b89e1f 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -302,6 +302,7 @@ add_library(cudf src/reshape/tile.cu src/rolling/grouped_rolling.cu src/rolling/rolling.cu + src/rolling/range_window_bounds.cpp src/round/round.cu src/scalar/scalar.cpp src/scalar/scalar_factories.cpp diff --git a/cpp/include/cudf/rolling.hpp b/cpp/include/cudf/rolling.hpp index 44a64a01c5e..8a2498d0163 100644 --- a/cpp/include/cudf/rolling.hpp +++ b/cpp/include/cudf/rolling.hpp @@ -16,6 +16,7 @@ #pragma once +#include #include #include @@ -119,6 +120,7 @@ struct window_bounds { { } }; + /** * @brief Applies a grouping-aware, fixed-size rolling window function to the values in a column. * @@ -264,7 +266,7 @@ std::unique_ptr grouped_rolling_window( /** * @brief Applies a grouping-aware, timestamp-based rolling window function to the values in a - *column. + * column. * * Like `rolling_window()`, this function aggregates values in a window around each * element of a specified `input` column. It differs from `rolling_window()` in two respects: @@ -357,16 +359,36 @@ std::unique_ptr grouped_time_range_rolling_window( rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** - * @copydoc std::unique_ptr grouped_time_range_rolling_window( - * table_view const& group_keys, - * column_view const& timestamp_column, - * cudf::order const& timestamp_order, - * column_view const& input, - * size_type preceding_window_in_days, - * size_type following_window_in_days, - * size_type min_periods, - * std::unique_ptr const& aggr, - * rmm::mr::device_memory_resource* mr) + * @brief Applies a grouping-aware, timestamp-based rolling window function to the values in a + * column,. + * + * @copydetails std::unique_ptr grouped_time_range_rolling_window( + * table_view const& group_keys, + * column_view const& timestamp_column, + * cudf::order const& timestamp_order, + * column_view const& input, + * size_type preceding_window_in_days, + * size_type following_window_in_days, + * size_type min_periods, + * std::unique_ptr const& aggr, + * rmm::mr::device_memory_resource* mr) + * + * The `preceding_window_in_days` and `following_window_in_days` supports "unbounded" windows, + * if set to `window_bounds::unbounded()`. + * + * @param[in] group_keys The (pre-sorted) grouping columns + * @param[in] timestamp_column The (pre-sorted) timestamps for each row + * @param[in] timestamp_order The order (ASCENDING/DESCENDING) in which the timestamps are sorted + * @param[in] input The input column (to be aggregated) + * @param[in] preceding_window_in_days Possibly unbounded time-interval in the backward direction, + * specified as a `window_bounds` + * @param[in] following_window_in_days Possibly unbounded time-interval in the forward direction, + * specified as a `window_bounds` + * @param[in] min_periods Minimum number of observations in window required to have a value, + * otherwise element `i` is null. + * @param[in] aggr The rolling window aggregation type (SUM, MAX, MIN, etc.) + * + * @returns A nullable output column containing the rolling window results */ std::unique_ptr grouped_time_range_rolling_window( table_view const& group_keys, @@ -379,6 +401,125 @@ std::unique_ptr grouped_time_range_rolling_window( std::unique_ptr const& aggr, rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); +/** + * @brief Applies a grouping-aware, value range-based rolling window function to the values in a + * column. + * + * This function aggregates rows in a window around each element of a specified `input` column. + * The window is determined based on the values of an ordered `orderby` column, and on the values + * of a `preceding` and `following` scalar representing an inclusive range of orderby column values. + * + * 1. The elements of the `input` column are grouped into distinct groups (e.g. the result of a + * groupby), determined by the corresponding values of the columns under `group_keys`. The + * window-aggregation cannot cross the group boundaries. + * 2. Within a group, with all rows sorted by the `orderby` column, the aggregation window + * for a row at index `i` is determined as follows: + * a) If `orderby` is ASCENDING, aggregation window for row `i` includes all `input` rows at + * index `j` such that: + * @code{.pseudo} + * (orderby[i] - preceding) <= orderby[j] <= orderby[i] + following + * @endcode + * b) If `orderby` is DESCENDING, aggregation window for row `i` includes all `input` rows at + * index `j` such that: + * @code{.pseudo} + * (orderby[i] + preceding) >= orderby[j] >= orderby[i] - following + * @endcode + * + * Note: This method requires that the rows are presorted by the group keys and orderby column + * values. + * + * The window intervals are specified as scalar values appropriate for the orderby column. + * Currently, only the following combinations of `orderby` column type and range types + * are supported: + * 1. If `orderby` column is a TIMESTAMP, the `preceding`/`following` windows are specified + * in terms of `DURATION` scalars of the same resolution. + * E.g. For `orderby` column of type `TIMESTAMP_SECONDS`, the intervals may only be + * `DURATION_SECONDS`. Durations of higher resolution (e.g. `DURATION_NANOSECONDS`) + * or lower (e.g. `DURATION_DAYS`) cannot be used. + * 2. If the `orderby` column is an integral type (e.g. `INT32`), the `preceding`/`following` + * should be the exact same type (`INT32`). + * + * @code{.pseudo} + * Example: Consider an motor-racing statistics dataset, containing the following columns: + * 1. driver_name: (STRING) Name of the car driver + * 2. num_overtakes: (INT32) Number of times the driver overtook another car in a lap + * 3. lap_number: (INT32) The number of the lap + * + * The `group_range_rolling_window()` function allows one to calculate the total number of overtakes + * each driver made within any 3 lap window of each entry: + * 1. Group/partition the dataset by `driver_id` (This is the group_keys argument.) + * 2. Sort each group by the `lap_number` (i.e. This is the orderby_column.) + * 3. Calculate the SUM(num_overtakes) over a window (preceding=1, following=1) + * + * For the following input: + * + * [ // driver_name, num_overtakes, lap_number + * { "bottas", 1, 1 }, + * { "hamilton", 2, 1 }, + * { "bottas", 2, 2 }, + * { "bottas", 1, 3 }, + * { "hamilton", 3, 1 }, + * { "hamilton", 8, 2 }, + * { "bottas", 5, 7 }, + * { "bottas", 6, 8 }, + * { "hamilton", 4, 4 } + * ] + * + * Partitioning (grouping) by `driver_name`, and ordering by `lap_number` yields the following + * `num_overtakes` vector (with 2 groups, one for each distinct `driver_name`): + * + * lap_number: [ 1, 2, 3, 7, 8, 1, 1, 2, 4 ] + * num_overtakes: [ 1, 2, 1, 5, 6, 2, 3, 8, 4 ] + * <-----bottas------>|<----hamilton---> + * + * The SUM aggregation is applied, with 1 preceding, and 1 following, with a minimum of 1 + * period. The aggregation window is thus 3 (laps) wide, yielding the following output column: + * + * Results: [ 3, 4, 3, 11, 11, 13, 13, 13, 4 ] + * + * @endcode + * + * Note: The number of rows participating in each window might vary, based on the index within the + * group, datestamp, and `min_periods`. Apropos: + * 1. results[0] considers 2 values, because it is at the beginning of its group, and has no + * preceding values. + * 2. results[5] considers 3 values, despite being at the beginning of its group. It must include 2 + * following values, based on its orderby_column value. + * + * Each aggregation operation cannot cross group boundaries. + * + * The type of the returned column depends on the input column type `T`, and the aggregation: + * 1. COUNT returns `INT32` columns + * 2. MIN/MAX returns `T` columns + * 3. SUM returns the promoted type for T. Sum on `INT32` yields `INT64`. + * 4. MEAN returns FLOAT64 columns + * 5. COLLECT returns columns of type `LIST`. + * + * LEAD/LAG/ROW_NUMBER are undefined for range queries. + * + * @param[in] group_keys The (pre-sorted) grouping columns + * @param[in] orderby_column The (pre-sorted) order-by column, for range comparisons + * @param[in] order The order (ASCENDING/DESCENDING) in which the order-by column is sorted + * @param[in] input The input column (to be aggregated) + * @param[in] preceding The interval value in the backward direction + * @param[in] following The interval value in the forward direction. + * @param[in] min_periods Minimum number of observations in window required to have a value, + * otherwise element `i` is null. + * @param[in] aggr The rolling window aggregation type (SUM, MAX, MIN, etc.) + * + * @returns A nullable output column containing the rolling window results + */ +std::unique_ptr grouped_range_rolling_window( + table_view const& group_keys, + column_view const& orderby_column, + cudf::order const& order, + column_view const& input, + range_window_bounds const& preceding, + range_window_bounds const& following, + size_type min_periods, + std::unique_ptr const& aggr, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + /** * @brief Applies a variable-size rolling window function to the values in a column. * diff --git a/cpp/include/cudf/rolling/range_window_bounds.hpp b/cpp/include/cudf/rolling/range_window_bounds.hpp new file mode 100644 index 00000000000..0c86bd3cf86 --- /dev/null +++ b/cpp/include/cudf/rolling/range_window_bounds.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace cudf { + +/** + * @brief Abstraction for window boundary sizes, to be used with + * `grouped_range_rolling_window()`. + * + * Similar to `window_bounds` in `grouped_rolling_window()`, `range_window_bounds` + * represents window boundaries for use with `grouped_range_rolling_window()`. + * A window may be specified as either of the following: + * 1. A fixed-width numeric scalar value. E.g. + * a) A `DURATION_DAYS` scalar, for use with a `TIMESTAMP_DAYS` orderby column + * b) An `INT32` scalar, for use with an `INT32` orderby column + * 2. "unbounded", indicating that the bounds stretch to the first/last + * row in the group. + */ +struct range_window_bounds { + public: + /** + * @brief Factory method to construct a bounded window boundary. + * + * @param value Finite window boundary + * + */ + static range_window_bounds get(scalar const&); + + /** + * @brief Factory method to construct an unbounded window boundary. + * + * @param @type The datatype of the window boundary + */ + static range_window_bounds unbounded(data_type type); + + /** + * @brief Whether or not the window is unbounded + * + * @return true If window is unbounded + * @return false If window is of finite bounds + */ + bool is_unbounded() const { return _is_unbounded; } + + /** + * @brief Returns the underlying scalar value for the bounds + */ + scalar const& range_scalar() const { return *_range_scalar; } + + range_window_bounds(range_window_bounds const&) = + default; // Required to return (by copy) from functions. + range_window_bounds() = default; // Required for use as return types from dispatch functors. + + private: + const bool _is_unbounded{true}; + std::shared_ptr _range_scalar{nullptr}; // To enable copy construction/assignment. + + range_window_bounds(bool is_unbounded_, std::unique_ptr range_scalar_); +}; + +} // namespace cudf diff --git a/cpp/src/rolling/grouped_rolling.cu b/cpp/src/rolling/grouped_rolling.cu index ca4913c1843..e2f2e8b07dc 100644 --- a/cpp/src/rolling/grouped_rolling.cu +++ b/cpp/src/rolling/grouped_rolling.cu @@ -14,10 +14,13 @@ * limitations under the License. */ +#include "range_window_bounds_detail.hpp" #include "rolling_detail.cuh" #include "rolling_jit_detail.hpp" #include +#include +#include #include namespace cudf { @@ -205,57 +208,78 @@ std::unique_ptr grouped_rolling_window(table_view const& group_keys, namespace { -bool is_supported_range_frame_unit(cudf::data_type const& data_type) +/// For order-by columns of signed types, bounds calculation might cause accidental +/// overflow/underflows. This needs to be detected and handled appropriately +/// for signed and unsigned types. + +/** + * @brief Add `delta` to value, and cap at numeric_limits::max(), for signed types. + */ +template ::is_signed>* = nullptr> +__device__ T add_safe(T const& value, T const& delta) { - auto id = data_type.id(); - return id == cudf::type_id::TIMESTAMP_DAYS || id == cudf::type_id::TIMESTAMP_SECONDS || - id == cudf::type_id::TIMESTAMP_MILLISECONDS || - id == cudf::type_id::TIMESTAMP_MICROSECONDS || id == cudf::type_id::TIMESTAMP_NANOSECONDS; + // delta >= 0. + return (value < 0 || (std::numeric_limits::max() - value) >= delta) + ? (value + delta) + : std::numeric_limits::max(); } -/// Fetches multiplication factor to normalize window sizes, depending on the datatype of the -/// timestamp column. Used for time-based rolling-window operations. E.g. If the timestamp column is -/// in TIMESTAMP_SECONDS, and the window sizes are specified in DAYS, the window size needs to be -/// multiplied by `24*60*60`, before comparisons with the timestamps. -size_t multiplication_factor(cudf::data_type const& data_type) +/** + * @brief Add `delta` to value, and cap at numeric_limits::max(), for unsigned types. + */ +template ::is_signed>* = nullptr> +__device__ T add_safe(T const& value, T const& delta) { - // Assume timestamps. - switch (data_type.id()) { - case cudf::type_id::TIMESTAMP_DAYS: return 1L; - case cudf::type_id::TIMESTAMP_SECONDS: return 24L * 60 * 60; - case cudf::type_id::TIMESTAMP_MILLISECONDS: return 24L * 60 * 60 * 1000; - case cudf::type_id::TIMESTAMP_MICROSECONDS: return 24L * 60 * 60 * 1000 * 1000; - case cudf::type_id::TIMESTAMP_NANOSECONDS: return 24L * 60 * 60 * 1000 * 1000 * 1000; - default: - CUDF_FAIL("Unexpected data-type for timestamp-based rolling window operation!"); - return {}; - } + // delta >= 0. + return ((std::numeric_limits::max() - value) >= delta) ? (value + delta) + : std::numeric_limits::max(); } -/// Given a single, ungrouped timestamp column, return the indices corresponding -/// to the first null timestamp, and (one past) the last null timestamp. +/** + * @brief Subtract `delta` from value, and cap at numeric_limits::min(), for signed types. + */ +template ::is_signed>* = nullptr> +__device__ T subtract_safe(T const& value, T const& delta) +{ + // delta >= 0; + return (value >= 0 || (value - std::numeric_limits::min()) >= delta) + ? (value - delta) + : std::numeric_limits::min(); +} + +/** + * @brief Subtract `delta` from value, and cap at numeric_limits::min(), for unsigned types. + */ +template ::is_signed>* = nullptr> +__device__ T subtract_safe(T const& value, T const& delta) +{ + // delta >= 0; + return ((value - std::numeric_limits::min()) >= delta) ? (value - delta) + : std::numeric_limits::min(); +} + +/// Given a single, ungrouped order-by column, return the indices corresponding +/// to the first null element, and (one past) the last null timestamp. /// The input column is sorted, with all null values clustered either /// at the beginning of the column or at the end. /// If no null values are founds, null_begin and null_end are 0. -std::tuple get_null_bounds_for_timestamp_column( - column_view const& timestamp_column) +std::tuple get_null_bounds_for_orderby_column( + column_view const& orderby_column) { - auto const num_rows = timestamp_column.size(); - auto const num_nulls = timestamp_column.null_count(); + auto const num_rows = orderby_column.size(); + auto const num_nulls = orderby_column.null_count(); if (num_nulls == num_rows || num_nulls == 0) { // Short-circuit: All nulls, or no nulls. return std::make_tuple(0, num_nulls); } - auto const first_row_is_null = timestamp_column.null_count(0, 1) == 1; + auto const first_row_is_null = orderby_column.null_count(0, 1) == 1; return first_row_is_null ? std::make_tuple(0, num_nulls) : std::make_tuple(num_rows - num_nulls, num_rows); } -using TimeT = int64_t; // Timestamp representations normalized to int64_t. - template std::unique_ptr expand_to_column(Calculator const& calc, size_type const& num_rows, @@ -273,28 +297,29 @@ std::unique_ptr expand_to_column(Calculator const& calc, return window_column; } -/// Time-range window computation, with +/// Range window computation, with /// 1. no grouping keys specified -/// 2. timetamps in ASCENDING order. +/// 2. rows in ASCENDING order. /// Treat as one single group. -std::unique_ptr time_range_window_ASC(column_view const& input, - column_view const& timestamp_column, - TimeT preceding_window, - bool preceding_window_is_unbounded, - TimeT following_window, - bool following_window_is_unbounded, - size_type min_periods, - std::unique_ptr const& aggr, - rmm::cuda_stream_view stream, - rmm::mr::device_memory_resource* mr) +template +std::unique_ptr range_window_ASC(column_view const& input, + column_view const& orderby_column, + T preceding_window, + bool preceding_window_is_unbounded, + T following_window, + bool following_window_is_unbounded, + size_type min_periods, + std::unique_ptr const& aggr, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) { size_type nulls_begin_idx, nulls_end_idx; - std::tie(nulls_begin_idx, nulls_end_idx) = get_null_bounds_for_timestamp_column(timestamp_column); + std::tie(nulls_begin_idx, nulls_end_idx) = get_null_bounds_for_orderby_column(orderby_column); auto preceding_calculator = [nulls_begin_idx, nulls_end_idx, - d_timestamps = timestamp_column.data(), + d_orderby = orderby_column.data(), preceding_window, preceding_window_is_unbounded] __device__(size_type idx) -> size_type { if (preceding_window_is_unbounded) { @@ -308,18 +333,18 @@ std::unique_ptr time_range_window_ASC(column_view const& input, return idx - nulls_begin_idx + 1; } - // timestamp[idx] not null. Binary search the group, excluding null group. + // orderby[idx] not null. Binary search the group, excluding null group. // If nulls_begin_idx == 0, either // 1. NULLS FIRST ordering: Binary search starts where nulls_end_idx. // 2. NO NULLS: Binary search starts at 0 (also nulls_end_idx). // Otherwise, NULLS LAST ordering. Start at 0. - auto group_start = nulls_begin_idx == 0 ? nulls_end_idx : 0; - auto lowest_timestamp_in_window = d_timestamps[idx] - preceding_window; + auto group_start = nulls_begin_idx == 0 ? nulls_end_idx : 0; + auto lowest_in_window = subtract_safe(d_orderby[idx], preceding_window); - return ((d_timestamps + idx) - thrust::lower_bound(thrust::seq, - d_timestamps + group_start, - d_timestamps + idx, - lowest_timestamp_in_window)) + + return ((d_orderby + idx) - thrust::lower_bound(thrust::seq, + d_orderby + group_start, + d_orderby + idx, + lowest_in_window)) + 1; // Add 1, for `preceding` to account for current row. }; @@ -328,8 +353,8 @@ std::unique_ptr time_range_window_ASC(column_view const& input, auto following_calculator = [nulls_begin_idx, nulls_end_idx, - num_rows = input.size(), - d_timestamps = timestamp_column.data(), + num_rows = input.size(), + d_orderby = orderby_column.data(), following_window, following_window_is_unbounded] __device__(size_type idx) -> size_type { if (following_window_is_unbounded) { return num_rows - idx - 1; } @@ -339,20 +364,18 @@ std::unique_ptr time_range_window_ASC(column_view const& input, return nulls_end_idx - idx - 1; } - // timestamp[idx] not null. Binary search the group, excluding null group. + // orderby[idx] not null. Binary search the group, excluding null group. // If nulls_begin_idx == 0, either // 1. NULLS FIRST ordering: Binary search ends at num_rows. // 2. NO NULLS: Binary search also ends at num_rows. // Otherwise, NULLS LAST ordering. End at nulls_begin_idx. - auto group_end = nulls_begin_idx == 0 ? num_rows : nulls_begin_idx; - auto highest_timestamp_in_window = d_timestamps[idx] + following_window; + auto group_end = nulls_begin_idx == 0 ? num_rows : nulls_begin_idx; + auto highest_in_window = add_safe(d_orderby[idx], following_window); - return (thrust::upper_bound(thrust::seq, - d_timestamps + idx, - d_timestamps + group_end, - highest_timestamp_in_window) - - (d_timestamps + idx)) - + return (thrust::upper_bound( + thrust::seq, d_orderby + idx, d_orderby + group_end, highest_in_window) - + (d_orderby + idx)) - 1; }; @@ -362,18 +385,19 @@ std::unique_ptr time_range_window_ASC(column_view const& input, input, preceding_column->view(), following_column->view(), min_periods, aggr, mr); } -/// Given a timestamp column grouped as specified in group_offsets, +/// Given an orderby column grouped as specified in group_offsets, /// return the following two vectors: /// 1. Vector with one entry per group, indicating the offset in the group /// where the null values begin. /// 2. Vector with one entry per group, indicating the offset in the group /// where the null values end. (i.e. 1 past the last null.) -/// Each group in the input timestamp column must be sorted, +/// Each group in the input orderby column must be sorted, /// with null values clustered at either the start or the end of each group. /// If there are no nulls for any given group, (nulls_begin, nulls_end) == (0,0). std::tuple, rmm::device_vector> -get_null_bounds_for_timestamp_column(column_view const& timestamp_column, - rmm::device_uvector const& group_offsets) +get_null_bounds_for_orderby_column(column_view const& orderby_column, + rmm::device_uvector const& group_offsets, + rmm::cuda_stream_view stream) { // For each group, the null values are themselves clustered // at the beginning or the end of the group. @@ -384,23 +408,23 @@ get_null_bounds_for_timestamp_column(column_view const& timestamp_column, auto null_start = rmm::device_vector(group_offsets.begin(), group_offsets.end() - 1); auto null_end = rmm::device_vector(group_offsets.begin(), group_offsets.end() - 1); - if (timestamp_column.has_nulls()) { - auto p_timestamps_device_view = column_device_view::create(timestamp_column); - auto num_groups = group_offsets.size() - 1; + if (orderby_column.has_nulls()) { + auto p_orderby_device_view = column_device_view::create(orderby_column); + auto num_groups = group_offsets.size() - 1; // Null timestamps exist. Find null bounds, per group. thrust::for_each( - thrust::device, + rmm::exec_policy(stream), thrust::make_counting_iterator(static_cast(0)), thrust::make_counting_iterator(static_cast(num_groups)), - [d_timestamps = *p_timestamps_device_view, + [d_orderby = *p_orderby_device_view, d_group_offsets = group_offsets.data(), d_null_start = null_start.data(), d_null_end = null_end.data()] __device__(auto group_label) { auto group_start = d_group_offsets[group_label]; auto group_end = d_group_offsets[group_label + 1]; - auto first_element_is_null = d_timestamps.is_null_nocheck(group_start); - auto last_element_is_null = d_timestamps.is_null_nocheck(group_end - 1); + auto first_element_is_null = d_orderby.is_null_nocheck(group_start); + auto last_element_is_null = d_orderby.is_null_nocheck(group_end - 1); if (!first_element_is_null && !last_element_is_null) { // Short circuit: No nulls. d_null_start[group_label] = group_start; @@ -416,7 +440,7 @@ get_null_bounds_for_timestamp_column(column_view const& timestamp_column, thrust::seq, thrust::make_counting_iterator(group_start), thrust::make_counting_iterator(group_end), - [&d_timestamps] __device__(auto i) { return d_timestamps.is_null_nocheck(i); }); + [&d_orderby] __device__(auto i) { return d_orderby.is_null_nocheck(i); }); } else { // NULLS LAST. d_null_end[group_label] = group_end; @@ -424,7 +448,7 @@ get_null_bounds_for_timestamp_column(column_view const& timestamp_column, thrust::seq, thrust::make_counting_iterator(group_start), thrust::make_counting_iterator(group_end), - [&d_timestamps] __device__(auto i) { return d_timestamps.is_valid_nocheck(i); }); + [&d_orderby] __device__(auto i) { return d_orderby.is_valid_nocheck(i); }); } }); } @@ -432,29 +456,29 @@ get_null_bounds_for_timestamp_column(column_view const& timestamp_column, return std::make_tuple(std::move(null_start), std::move(null_end)); } -// Time-range window computation, for timestamps in ASCENDING order. -std::unique_ptr time_range_window_ASC( - column_view const& input, - column_view const& timestamp_column, - rmm::device_uvector const& group_offsets, - rmm::device_uvector const& group_labels, - TimeT preceding_window, - bool preceding_window_is_unbounded, - TimeT following_window, - bool following_window_is_unbounded, - size_type min_periods, - std::unique_ptr const& aggr, - rmm::cuda_stream_view stream, - rmm::mr::device_memory_resource* mr) +// Range window computation, for orderby column in ASCENDING order. +template +std::unique_ptr range_window_ASC(column_view const& input, + column_view const& orderby_column, + rmm::device_uvector const& group_offsets, + rmm::device_uvector const& group_labels, + T preceding_window, + bool preceding_window_is_unbounded, + T following_window, + bool following_window_is_unbounded, + size_type min_periods, + std::unique_ptr const& aggr, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) { rmm::device_vector null_start, null_end; std::tie(null_start, null_end) = - get_null_bounds_for_timestamp_column(timestamp_column, group_offsets); + get_null_bounds_for_orderby_column(orderby_column, group_offsets, stream); auto preceding_calculator = [d_group_offsets = group_offsets.data(), d_group_labels = group_labels.data(), - d_timestamps = timestamp_column.data(), + d_orderby = orderby_column.data(), d_nulls_begin = null_start.data().get(), d_nulls_end = null_end.data().get(), preceding_window, @@ -473,19 +497,19 @@ std::unique_ptr time_range_window_ASC( return idx - nulls_begin + 1; } - // timestamp[idx] not null. Search must exclude the null group. + // orderby[idx] not null. Search must exclude the null group. // If nulls_begin == group_start, either of the following is true: // 1. NULLS FIRST ordering: Search must begin at nulls_end. // 2. NO NULLS: Search must begin at group_start (which also equals nulls_end.) // Otherwise, NULLS LAST ordering. Search must start at nulls group_start. auto search_start = nulls_begin == group_start ? nulls_end : group_start; - auto lowest_timestamp_in_window = d_timestamps[idx] - preceding_window; + auto lowest_in_window = subtract_safe(d_orderby[idx], preceding_window); - return ((d_timestamps + idx) - thrust::lower_bound(thrust::seq, - d_timestamps + search_start, - d_timestamps + idx, - lowest_timestamp_in_window)) + + return ((d_orderby + idx) - thrust::lower_bound(thrust::seq, + d_orderby + search_start, + d_orderby + idx, + lowest_in_window)) + 1; // Add 1, for `preceding` to account for current row. }; @@ -494,7 +518,7 @@ std::unique_ptr time_range_window_ASC( auto following_calculator = [d_group_offsets = group_offsets.data(), d_group_labels = group_labels.data(), - d_timestamps = timestamp_column.data(), + d_orderby = orderby_column.data(), d_nulls_begin = null_start.data().get(), d_nulls_end = null_end.data().get(), following_window, @@ -516,20 +540,18 @@ std::unique_ptr time_range_window_ASC( return nulls_end - idx - 1; } - // timestamp[idx] not null. Search must exclude the null group. + // orderby[idx] not null. Search must exclude the null group. // If nulls_begin == group_start, either of the following is true: // 1. NULLS FIRST ordering: Search ends at group_end. // 2. NO NULLS: Search ends at group_end. // Otherwise, NULLS LAST ordering. Search ends at nulls_begin. auto search_end = nulls_begin == group_start ? group_end : nulls_begin; - auto highest_timestamp_in_window = d_timestamps[idx] + following_window; + auto highest_in_window = add_safe(d_orderby[idx], following_window); - return (thrust::upper_bound(thrust::seq, - d_timestamps + idx, - d_timestamps + search_end, - highest_timestamp_in_window) - - (d_timestamps + idx)) - + return (thrust::upper_bound( + thrust::seq, d_orderby + idx, d_orderby + search_end, highest_in_window) - + (d_orderby + idx)) - 1; }; @@ -539,28 +561,29 @@ std::unique_ptr time_range_window_ASC( input, preceding_column->view(), following_column->view(), min_periods, aggr, mr); } -/// Time-range window computation, with +/// Range window computation, with /// 1. no grouping keys specified -/// 2. timetamps in DESCENDING order. +/// 2. rows in DESCENDING order. /// Treat as one single group. -std::unique_ptr time_range_window_DESC(column_view const& input, - column_view const& timestamp_column, - TimeT preceding_window, - bool preceding_window_is_unbounded, - TimeT following_window, - bool following_window_is_unbounded, - size_type min_periods, - std::unique_ptr const& aggr, - rmm::cuda_stream_view stream, - rmm::mr::device_memory_resource* mr) +template +std::unique_ptr range_window_DESC(column_view const& input, + column_view const& orderby_column, + T preceding_window, + bool preceding_window_is_unbounded, + T following_window, + bool following_window_is_unbounded, + size_type min_periods, + std::unique_ptr const& aggr, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) { size_type nulls_begin_idx, nulls_end_idx; - std::tie(nulls_begin_idx, nulls_end_idx) = get_null_bounds_for_timestamp_column(timestamp_column); + std::tie(nulls_begin_idx, nulls_end_idx) = get_null_bounds_for_orderby_column(orderby_column); auto preceding_calculator = [nulls_begin_idx, nulls_end_idx, - d_timestamps = timestamp_column.data(), + d_orderby = orderby_column.data(), preceding_window, preceding_window_is_unbounded] __device__(size_type idx) -> size_type { if (preceding_window_is_unbounded) { @@ -574,20 +597,20 @@ std::unique_ptr time_range_window_DESC(column_view const& input, return idx - nulls_begin_idx + 1; } - // timestamp[idx] not null. Binary search the group, excluding null group. + // orderby[idx] not null. Binary search the group, excluding null group. // If nulls_begin_idx == 0, either // 1. NULLS FIRST ordering: Binary search starts where nulls_end_idx. // 2. NO NULLS: Binary search starts at 0 (also nulls_end_idx). // Otherwise, NULLS LAST ordering. Start at 0. - auto group_start = nulls_begin_idx == 0 ? nulls_end_idx : 0; - auto highest_timestamp_in_window = d_timestamps[idx] + preceding_window; + auto group_start = nulls_begin_idx == 0 ? nulls_end_idx : 0; + auto highest_in_window = add_safe(d_orderby[idx], preceding_window); - return ((d_timestamps + idx) - + return ((d_orderby + idx) - thrust::lower_bound(thrust::seq, - d_timestamps + group_start, - d_timestamps + idx, - highest_timestamp_in_window, - thrust::greater())) + + d_orderby + group_start, + d_orderby + idx, + highest_in_window, + thrust::greater())) + 1; // Add 1, for `preceding` to account for current row. }; @@ -596,8 +619,8 @@ std::unique_ptr time_range_window_DESC(column_view const& input, auto following_calculator = [nulls_begin_idx, nulls_end_idx, - num_rows = input.size(), - d_timestamps = timestamp_column.data(), + num_rows = input.size(), + d_orderby = orderby_column.data(), following_window, following_window_is_unbounded] __device__(size_type idx) -> size_type { if (following_window_is_unbounded) { return (num_rows - idx) - 1; } @@ -607,21 +630,21 @@ std::unique_ptr time_range_window_DESC(column_view const& input, return nulls_end_idx - idx - 1; } - // timestamp[idx] not null. Search must exclude null group. + // orderby[idx] not null. Search must exclude null group. // If nulls_begin_idx = 0, either // 1. NULLS FIRST ordering: Search ends at num_rows. // 2. NO NULLS: Search also ends at num_rows. // Otherwise, NULLS LAST ordering: End at nulls_begin_idx. - auto group_end = nulls_begin_idx == 0 ? num_rows : nulls_begin_idx; - auto lowest_timestamp_in_window = d_timestamps[idx] - following_window; + auto group_end = nulls_begin_idx == 0 ? num_rows : nulls_begin_idx; + auto lowest_in_window = subtract_safe(d_orderby[idx], following_window); return (thrust::upper_bound(thrust::seq, - d_timestamps + idx, - d_timestamps + group_end, - lowest_timestamp_in_window, - thrust::greater()) - - (d_timestamps + idx)) - + d_orderby + idx, + d_orderby + group_end, + lowest_in_window, + thrust::greater()) - + (d_orderby + idx)) - 1; }; @@ -631,29 +654,29 @@ std::unique_ptr time_range_window_DESC(column_view const& input, input, preceding_column->view(), following_column->view(), min_periods, aggr, mr); } -// Time-range window computation, for timestamps in DESCENDING order. -std::unique_ptr time_range_window_DESC( - column_view const& input, - column_view const& timestamp_column, - rmm::device_uvector const& group_offsets, - rmm::device_uvector const& group_labels, - TimeT preceding_window, - bool preceding_window_is_unbounded, - TimeT following_window, - bool following_window_is_unbounded, - size_type min_periods, - std::unique_ptr const& aggr, - rmm::cuda_stream_view stream, - rmm::mr::device_memory_resource* mr) +// Range window computation, for rows in DESCENDING order. +template +std::unique_ptr range_window_DESC(column_view const& input, + column_view const& orderby_column, + rmm::device_uvector const& group_offsets, + rmm::device_uvector const& group_labels, + T preceding_window, + bool preceding_window_is_unbounded, + T following_window, + bool following_window_is_unbounded, + size_type min_periods, + std::unique_ptr const& aggr, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) { rmm::device_vector null_start, null_end; std::tie(null_start, null_end) = - get_null_bounds_for_timestamp_column(timestamp_column, group_offsets); + get_null_bounds_for_orderby_column(orderby_column, group_offsets, stream); auto preceding_calculator = [d_group_offsets = group_offsets.data(), d_group_labels = group_labels.data(), - d_timestamps = timestamp_column.data(), + d_orderby = orderby_column.data(), d_nulls_begin = null_start.data().get(), d_nulls_end = null_end.data().get(), preceding_window, @@ -672,21 +695,21 @@ std::unique_ptr time_range_window_DESC( return idx - nulls_begin + 1; } - // timestamp[idx] not null. Search must exclude the null group. + // orderby[idx] not null. Search must exclude the null group. // If nulls_begin == group_start, either of the following is true: // 1. NULLS FIRST ordering: Search must begin at nulls_end. // 2. NO NULLS: Search must begin at group_start (which also equals nulls_end.) // Otherwise, NULLS LAST ordering. Search must start at nulls group_start. auto search_start = nulls_begin == group_start ? nulls_end : group_start; - auto highest_timestamp_in_window = d_timestamps[idx] + preceding_window; + auto highest_in_window = add_safe(d_orderby[idx], preceding_window); - return ((d_timestamps + idx) - + return ((d_orderby + idx) - thrust::lower_bound(thrust::seq, - d_timestamps + search_start, - d_timestamps + idx, - highest_timestamp_in_window, - thrust::greater())) + + d_orderby + search_start, + d_orderby + idx, + highest_in_window, + thrust::greater())) + 1; // Add 1, for `preceding` to account for current row. }; @@ -695,7 +718,7 @@ std::unique_ptr time_range_window_DESC( auto following_calculator = [d_group_offsets = group_offsets.data(), d_group_labels = group_labels.data(), - d_timestamps = timestamp_column.data(), + d_orderby = orderby_column.data(), d_nulls_begin = null_start.data().get(), d_nulls_end = null_end.data().get(), following_window, @@ -715,115 +738,212 @@ std::unique_ptr time_range_window_DESC( return nulls_end - idx - 1; } - // timestamp[idx] not null. Search must exclude the null group. + // orderby[idx] not null. Search must exclude the null group. // If nulls_begin == group_start, either of the following is true: // 1. NULLS FIRST ordering: Search ends at group_end. // 2. NO NULLS: Search ends at group_end. // Otherwise, NULLS LAST ordering. Search ends at nulls_begin. auto search_end = nulls_begin == group_start ? group_end : nulls_begin; - auto lowest_timestamp_in_window = d_timestamps[idx] - following_window; + auto lowest_in_window = subtract_safe(d_orderby[idx], following_window); return (thrust::upper_bound(thrust::seq, - d_timestamps + idx, - d_timestamps + search_end, - lowest_timestamp_in_window, - thrust::greater()) - - (d_timestamps + idx)) - + d_orderby + idx, + d_orderby + search_end, + lowest_in_window, + thrust::greater()) - + (d_orderby + idx)) - 1; }; auto following_column = expand_to_column(following_calculator, input.size(), stream, mr); if (aggr->kind == aggregation::CUDA || aggr->kind == aggregation::PTX) { - CUDF_FAIL("Time ranged rolling window does NOT (yet) support UDF."); + CUDF_FAIL("Ranged rolling window does NOT (yet) support UDF."); } else { return cudf::rolling_window( input, preceding_column->view(), following_column->view(), min_periods, aggr, mr); } } -std::unique_ptr grouped_time_range_rolling_window_impl( +template +std::unique_ptr grouped_range_rolling_window_impl( column_view const& input, - column_view const& timestamp_column, + column_view const& orderby_column, cudf::order const& timestamp_ordering, rmm::device_uvector const& group_offsets, rmm::device_uvector const& group_labels, - window_bounds preceding_window_in_days, // TODO: Consider taking offset-type as type_id. Assumes - // days for now. - window_bounds following_window_in_days, + range_window_bounds preceding_window, + range_window_bounds following_window, size_type min_periods, std::unique_ptr const& aggr, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { - TimeT mult_factor{static_cast(multiplication_factor(timestamp_column.type()))}; + auto preceding_value = detail::range_comparable_value(preceding_window); + auto following_value = detail::range_comparable_value(following_window); if (timestamp_ordering == cudf::order::ASCENDING) { - return group_offsets.is_empty() - ? time_range_window_ASC(input, - timestamp_column, - preceding_window_in_days.value * mult_factor, - preceding_window_in_days.is_unbounded, - following_window_in_days.value * mult_factor, - following_window_in_days.is_unbounded, - min_periods, - aggr, - stream, - mr) - : time_range_window_ASC(input, - timestamp_column, - group_offsets, - group_labels, - preceding_window_in_days.value * mult_factor, - preceding_window_in_days.is_unbounded, - following_window_in_days.value * mult_factor, - following_window_in_days.is_unbounded, - min_periods, - aggr, - stream, - mr); + return group_offsets.is_empty() ? range_window_ASC(input, + orderby_column, + preceding_value, + preceding_window.is_unbounded(), + following_value, + following_window.is_unbounded(), + min_periods, + aggr, + stream, + mr) + : range_window_ASC(input, + orderby_column, + group_offsets, + group_labels, + preceding_value, + preceding_window.is_unbounded(), + following_value, + following_window.is_unbounded(), + min_periods, + aggr, + stream, + mr); } else { - return group_offsets.is_empty() - ? time_range_window_DESC(input, - timestamp_column, - preceding_window_in_days.value * mult_factor, - preceding_window_in_days.is_unbounded, - following_window_in_days.value * mult_factor, - following_window_in_days.is_unbounded, - min_periods, - aggr, - stream, - mr) - : time_range_window_DESC(input, - timestamp_column, - group_offsets, - group_labels, - preceding_window_in_days.value * mult_factor, - preceding_window_in_days.is_unbounded, - following_window_in_days.value * mult_factor, - following_window_in_days.is_unbounded, - min_periods, - aggr, - stream, - mr); + return group_offsets.is_empty() ? range_window_DESC(input, + orderby_column, + preceding_value, + preceding_window.is_unbounded(), + following_value, + following_window.is_unbounded(), + min_periods, + aggr, + stream, + mr) + : range_window_DESC(input, + orderby_column, + group_offsets, + group_labels, + preceding_value, + preceding_window.is_unbounded(), + following_value, + following_window.is_unbounded(), + min_periods, + aggr, + stream, + mr); } } +struct dispatch_grouped_range_rolling_window { + template + std::enable_if_t(), + std::unique_ptr> + operator()(Args&&...) const + { + CUDF_FAIL("Unsupported OrderBy column type."); + } + + template + std::enable_if_t(), + std::unique_ptr> + operator()(Args&&... args) const + { + return grouped_range_rolling_window_impl(std::forward(args)...); + } +}; + +/** + * @brief Functor to convert from size_type (number of days) to appropriate duration type. + */ +struct to_duration_bounds { + template (), void>* = nullptr> + range_window_bounds operator()(size_type num_days) const + { + using DurationT = typename OrderBy::duration; + return range_window_bounds::get(duration_scalar{duration_D{num_days}, true}); + } + + template (), void>* = nullptr> + range_window_bounds operator()(size_type num_days) const + { + CUDF_FAIL("Expected timestamp orderby column."); + } +}; + +/** + * @brief Get duration type corresponding to specified timestamp type. + */ +data_type get_duration_type_for(cudf::data_type timestamp_type) +{ + switch (timestamp_type.id()) { + case type_id::TIMESTAMP_DAYS: return data_type{type_id::DURATION_DAYS}; + case type_id::TIMESTAMP_SECONDS: return data_type{type_id::DURATION_SECONDS}; + case type_id::TIMESTAMP_MILLISECONDS: return data_type{type_id::DURATION_MILLISECONDS}; + case type_id::TIMESTAMP_MICROSECONDS: return data_type{type_id::DURATION_MICROSECONDS}; + case type_id::TIMESTAMP_NANOSECONDS: return data_type{type_id::DURATION_NANOSECONDS}; + default: CUDF_FAIL("Expected timestamp orderby column."); + } +} + +/** + * @brief Bridge function to convert from size_type (number of days) to appropriate duration type. + * + * This helps adapt the old `grouped_time_range_rolling_window()` functions that took a "number of + * days" to the new `range_window_bounds` interface. + * + * @param num_days Window bounds specified in number of days in `size_type` + * @param timestamp_type Data-type of the orderby column to which the `num_days` is to be adapted. + * @return range_window_bounds A `range_window_bounds` to be used with the new API. + */ +range_window_bounds to_range_bounds(cudf::size_type num_days, cudf::data_type timestamp_type) +{ + return cudf::type_dispatcher(timestamp_type, to_duration_bounds{}, num_days); +} + +/** + * @brief Bridge function to convert from `window_bounds` (in days) to appropriate duration type. + * + * This helps adapt the old `grouped_time_range_rolling_window()` functions that took a + * `window_bounds` to the new `range_window_bounds` interface. + * + * @param days_bounds The static window-width `window_bounds` object + * @param timestamp_type Data-type of the orderby column to which the `num_days` is to be adapted. + * @return range_window_bounds A `range_window_bounds` to be used with the new API. + */ +range_window_bounds to_range_bounds(cudf::window_bounds const& days_bounds, + cudf::data_type timestamp_type) +{ + return days_bounds.is_unbounded + ? range_window_bounds::unbounded(get_duration_type_for(timestamp_type)) + : cudf::type_dispatcher(timestamp_type, to_duration_bounds{}, days_bounds.value); +} + } // namespace namespace detail { -std::unique_ptr grouped_time_range_rolling_window(table_view const& group_keys, - column_view const& timestamp_column, - cudf::order const& timestamp_order, - column_view const& input, - window_bounds preceding_window_in_days, - window_bounds following_window_in_days, - size_type min_periods, - std::unique_ptr const& aggr, - rmm::cuda_stream_view stream, - rmm::mr::device_memory_resource* mr) +/** + * @copydoc std::unique_ptr grouped_range_rolling_window( + * table_view const& group_keys, + * column_view const& orderby_column, + * cudf::order const& order, + * column_view const& input, + * range_window_bounds const& preceding, + * range_window_bounds const& following, + * size_type min_periods, + * std::unique_ptr const& aggr, + * rmm::mr::device_memory_resource* mr ); + * + * @param stream CUDA stream used for device memory operations and kernel launches. + */ +std::unique_ptr grouped_range_rolling_window(table_view const& group_keys, + column_view const& order_by_column, + cudf::order const& order, + column_view const& input, + range_window_bounds const& preceding, + range_window_bounds const& following, + size_type min_periods, + std::unique_ptr const& aggr, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) { CUDF_FUNC_RANGE(); @@ -844,30 +964,35 @@ std::unique_ptr grouped_time_range_rolling_window(table_view const& grou group_labels = index_vector(helper.group_labels(stream), stream); } - // Assumes that `timestamp_column` is actually of a timestamp type. - CUDF_EXPECTS(is_supported_range_frame_unit(timestamp_column.type()), - "Unsupported data-type for `timestamp`-based rolling window operation!"); - - auto is_timestamp_in_days = timestamp_column.type().id() == cudf::type_id::TIMESTAMP_DAYS; - - return grouped_time_range_rolling_window_impl( - input, - is_timestamp_in_days - ? cudf::cast(timestamp_column, cudf::data_type(cudf::type_id::TIMESTAMP_SECONDS), mr)->view() - : timestamp_column, - timestamp_order, - group_offsets, - group_labels, - preceding_window_in_days, - following_window_in_days, - min_periods, - aggr, - stream, - mr); + return cudf::type_dispatcher(order_by_column.type(), + dispatch_grouped_range_rolling_window{}, + input, + order_by_column, + order, + group_offsets, + group_labels, + preceding, + following, + min_periods, + aggr, + stream, + mr); } } // namespace detail +/** + * @copydoc std::unique_ptr grouped_time_range_rolling_window( + * table_view const& group_keys, + * column_view const& timestamp_column, + * cudf::order const& timestamp_order, + * column_view const& input, + * size_type preceding_window_in_days, + * size_type following_window_in_days, + * size_type min_periods, + * std::unique_ptr const& aggr, + * rmm::mr::device_memory_resource* mr); + */ std::unique_ptr grouped_time_range_rolling_window(table_view const& group_keys, column_view const& timestamp_column, cudf::order const& timestamp_order, @@ -878,18 +1003,32 @@ std::unique_ptr grouped_time_range_rolling_window(table_view const& grou std::unique_ptr const& aggr, rmm::mr::device_memory_resource* mr) { - return grouped_time_range_rolling_window(group_keys, - timestamp_column, - timestamp_order, - input, - window_bounds::get(preceding_window_in_days), - window_bounds::get(following_window_in_days), - min_periods, - aggr, - rmm::cuda_stream_default, - mr); + auto preceding = to_range_bounds(preceding_window_in_days, timestamp_column.type()); + auto following = to_range_bounds(following_window_in_days, timestamp_column.type()); + + return grouped_range_rolling_window(group_keys, + timestamp_column, + timestamp_order, + input, + preceding, + following, + min_periods, + aggr, + mr); } +/** + * @copydoc std::unique_ptr grouped_time_range_rolling_window( + * table_view const& group_keys, + * column_view const& timestamp_column, + * cudf::order const& timestamp_order, + * column_view const& input, + * window_bounds preceding_window_in_days, + * window_bounds following_window_in_days, + * size_type min_periods, + * std::unique_ptr const& aggr, + * rmm::mr::device_memory_resource* mr); + */ std::unique_ptr grouped_time_range_rolling_window(table_view const& group_keys, column_view const& timestamp_column, cudf::order const& timestamp_order, @@ -900,16 +1039,55 @@ std::unique_ptr grouped_time_range_rolling_window(table_view const& grou std::unique_ptr const& aggr, rmm::mr::device_memory_resource* mr) { - return detail::grouped_time_range_rolling_window(group_keys, - timestamp_column, - timestamp_order, - input, - preceding_window_in_days, - following_window_in_days, - min_periods, - aggr, - rmm::cuda_stream_default, - mr); + range_window_bounds preceding = + to_range_bounds(preceding_window_in_days, timestamp_column.type()); + range_window_bounds following = + to_range_bounds(following_window_in_days, timestamp_column.type()); + + return grouped_range_rolling_window(group_keys, + timestamp_column, + timestamp_order, + input, + preceding, + following, + min_periods, + aggr, + rmm::cuda_stream_default, + mr); +} + +/** + * @copydoc std::unique_ptr grouped_range_rolling_window( + * table_view const& group_keys, + * column_view const& orderby_column, + * cudf::order const& order, + * column_view const& input, + * range_window_bounds const& preceding, + * range_window_bounds const& following, + * size_type min_periods, + * std::unique_ptr const& aggr, + * rmm::mr::device_memory_resource* mr ); + */ +std::unique_ptr grouped_range_rolling_window(table_view const& group_keys, + column_view const& timestamp_column, + cudf::order const& timestamp_order, + column_view const& input, + range_window_bounds const& preceding, + range_window_bounds const& following, + size_type min_periods, + std::unique_ptr const& aggr, + rmm::mr::device_memory_resource* mr) +{ + return detail::grouped_range_rolling_window(group_keys, + timestamp_column, + timestamp_order, + input, + preceding, + following, + min_periods, + aggr, + rmm::cuda_stream_default, + mr); } } // namespace cudf diff --git a/cpp/src/rolling/range_window_bounds.cpp b/cpp/src/rolling/range_window_bounds.cpp new file mode 100644 index 00000000000..92a2216eeef --- /dev/null +++ b/cpp/src/rolling/range_window_bounds.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "range_window_bounds_detail.hpp" + +namespace cudf { +namespace { + +/** + * @brief Factory to (copy) construct scalars. + * + * Derived types of scalars are cloned, to be adopted by `range_window_bounds`. + * This makes it possible to copy construct and copy assign `range_window_bounds` objects. + */ +struct range_scalar_constructor { + template (), void>* = nullptr> + std::unique_ptr operator()(scalar const& range_scalar_) const + { + CUDF_FAIL( + "Unsupported range type. " + "Only Durations and non-boolean integral range types are allowed."); + } + + template (), void>* = nullptr> + std::unique_ptr operator()(scalar const& range_scalar_) const + { + return std::make_unique>( + static_cast const&>(range_scalar_)); + } + + template ::value && !cudf::is_boolean(), void>* = nullptr> + std::unique_ptr operator()(scalar const& range_scalar_) const + { + return std::make_unique>( + static_cast const&>(range_scalar_)); + } +}; + +} // namespace + +range_window_bounds::range_window_bounds(bool is_unbounded_, std::unique_ptr range_scalar_) + : _is_unbounded{is_unbounded_}, _range_scalar{std::move(range_scalar_)} +{ + CUDF_EXPECTS(_range_scalar.get(), "Range window scalar cannot be null."); + CUDF_EXPECTS(_is_unbounded || _range_scalar->is_valid(), + "Bounded Range window scalar must be valid."); +} + +range_window_bounds range_window_bounds::unbounded(data_type type) +{ + return range_window_bounds(true, make_default_constructed_scalar(type)); +} + +range_window_bounds range_window_bounds::get(scalar const& scalar_) +{ + return range_window_bounds{ + false, cudf::type_dispatcher(scalar_.type(), range_scalar_constructor{}, scalar_)}; +} + +} // namespace cudf diff --git a/cpp/src/rolling/range_window_bounds_detail.hpp b/cpp/src/rolling/range_window_bounds_detail.hpp new file mode 100644 index 00000000000..47c9273a232 --- /dev/null +++ b/cpp/src/rolling/range_window_bounds_detail.hpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +namespace cudf { +namespace detail { + +/// Checks if the specified type is supported in a range_window_bounds. +template +constexpr bool is_supported_range_type() +{ + return cudf::is_duration() || + (std::is_integral::value && !cudf::is_boolean()); +} + +/// Checks if the specified type is a supported target type, +/// as an orderby column, for comparisons with a range_window_bounds scalar. +template +constexpr bool is_supported_order_by_column_type() +{ + return cudf::is_timestamp() || + (std::is_integral::value && !cudf::is_boolean()); +} + +/// Range-comparable representation type for an orderby column type. +/// This is the datatype used for range comparisons. +/// 1. For integral orderby column types `T`, comparisons are done as `T`. +/// E.g. `range_type_for` == `int32_t`. +/// 2. For timestamp orderby columns: +/// a. For `TIMESTAMP_DAYS`, the range-type is `DURATION_DAYS`. +/// Comparisons are done in `int32_t`. +/// b. For all other timestamp types, comparisons are done in `int64_t`. +template +struct range_type_impl { + using type = void; + using rep_type = void; +}; + +template +struct range_type_impl< + ColumnType, + std::enable_if_t::value && !cudf::is_boolean(), void>> { + using type = ColumnType; + using rep_type = ColumnType; +}; + +template +struct range_type_impl(), void>> { + using type = typename TimestampType::duration; + using rep_type = typename type::rep; +}; + +template +using range_type = typename range_type_impl::type; + +template +using range_rep_type = typename range_type_impl::rep_type; + +namespace { + +template +void assert_non_negative(T const& value) +{ + if constexpr (std::numeric_limits::is_signed) { + CUDF_EXPECTS(value >= T{0}, "Range scalar must be >= 0."); + } +} + +template < + typename RangeT, + typename RepT, + std::enable_if_t::value && !cudf::is_boolean(), void>* = nullptr> +RepT range_comparable_value_impl(scalar const& range_scalar, rmm::cuda_stream_view stream) +{ + auto val = static_cast const&>(range_scalar).value(stream); + assert_non_negative(val); + return val; +} + +template (), void>* = nullptr> +RepT range_comparable_value_impl(scalar const& range_scalar, rmm::cuda_stream_view stream) +{ + auto val = static_cast const&>(range_scalar).value(stream).count(); + assert_non_negative(val); + return val; +} + +} // namespace + +/** + * @brief Fetch the value of the range_window_bounds scalar, for comparisons + * with an orderby column's rows. + * + * @tparam OrderByType The type of the orderby column with which the range value will be compared + * @param range_bounds The range_window_bounds whose value is to be read + * @param stream The CUDA stream for device memory operations + * @return RepType Value of the range scalar + */ +template +range_rep_type range_comparable_value( + range_window_bounds const& range_bounds, rmm::cuda_stream_view stream = rmm::cuda_stream_default) +{ + auto const& range_scalar = range_bounds.range_scalar(); + using range_type = cudf::detail::range_type; + + CUDF_EXPECTS(range_scalar.type().id() == cudf::type_to_id(), + "Unexpected range type for specified orderby column."); + + using rep_type = cudf::detail::range_rep_type; + return range_comparable_value_impl(range_scalar, stream); +} + +} // namespace detail +} // namespace cudf diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 03b05eb07a7..7a3e14b4f12 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -272,8 +272,9 @@ ConfigureTest(ROLLING_TEST rolling/rolling_test.cpp rolling/grouped_rolling_test.cpp rolling/lead_lag_test.cpp - rolling/collect_list_test.cpp - ) + rolling/range_window_bounds_test.cpp + rolling/range_rolling_window_test.cpp + rolling/collect_list_test.cpp) ################################################################################################### # - filling test ---------------------------------------------------------------------------------- diff --git a/cpp/tests/rolling/range_rolling_window_test.cpp b/cpp/tests/rolling/range_rolling_window_test.cpp new file mode 100644 index 00000000000..48aa69f5816 --- /dev/null +++ b/cpp/tests/rolling/range_rolling_window_test.cpp @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace cudf { +namespace test { + +template +using fwcw = fixed_width_column_wrapper; + +using int_col = fwcw; +using size_col = fwcw; + +template +using time_col = fwcw; + +using lists_col = lists_column_wrapper; + +template +struct window_exec { + public: + window_exec(cudf::column_view gby, + cudf::column_view oby, + cudf::order ordering, + cudf::column_view agg, + ScalarT preceding_scalar, + ScalarT following_scalar, + cudf::size_type min_periods = 1) + : gby_column(gby), + oby_column(oby), + order(ordering), + agg_column(agg), + preceding(preceding_scalar), + following(following_scalar), + min_periods(min_periods) + { + } + + size_type num_rows() { return gby_column.size(); } + + std::unique_ptr operator()(std::unique_ptr const& agg) const + { + auto const grouping_keys = cudf::table_view{std::vector{gby_column}}; + + return cudf::grouped_range_rolling_window(grouping_keys, + oby_column, + order, + agg_column, + range_window_bounds::get(preceding), + range_window_bounds::get(following), + min_periods, + agg); + } + + private: + cudf::column_view gby_column; // Groupby column. + cudf::column_view oby_column; // Orderby column. + cudf::order order; // Ordering for `oby_column`. + cudf::column_view agg_column; // Aggregation column. + ScalarT preceding; // Preceding window scalar. + ScalarT following; // Following window scalar. + cudf::size_type min_periods = 1; +}; // struct window_exec; + +struct RangeRollingTest : public BaseFixture { +}; + +template +struct TypedTimeRangeRollingTest : RangeRollingTest { +}; + +TYPED_TEST_CASE(TypedTimeRangeRollingTest, cudf::test::TimestampTypes); + +template +void verify_results_for_ascending(WindowExecT exec) +{ + auto const n_rows = exec.num_rows(); + auto const all_valid = thrust::make_constant_iterator(true); + auto const all_invalid = thrust::make_constant_iterator(false); + auto const last_invalid = thrust::make_transform_iterator( + thrust::make_counting_iterator(0), [&n_rows](auto i) { return i != (n_rows - 1); }); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(exec(make_count_aggregation(null_policy::INCLUDE))->view(), + size_col{{1, 2, 2, 3, 2, 3, 3, 4, 4, 1}, all_valid}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(exec(make_count_aggregation())->view(), + size_col{{1, 2, 2, 3, 2, 3, 3, 4, 4, 0}, all_valid}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL( + exec(make_sum_aggregation())->view(), + fwcw{{0, 12, 12, 12, 8, 17, 17, 18, 18, 1}, last_invalid}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(exec(make_min_aggregation())->view(), + int_col{{0, 4, 4, 2, 2, 3, 3, 1, 1, 1}, last_invalid}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(exec(make_max_aggregation())->view(), + int_col{{0, 8, 8, 6, 6, 9, 9, 9, 9, 1}, last_invalid}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL( + exec(make_mean_aggregation())->view(), + fwcw{{0.0, 6.0, 6.0, 4.0, 4.0, 17.0 / 3, 17.0 / 3, 4.5, 4.5, 1.0}, last_invalid}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(exec(make_collect_list_aggregation())->view(), + lists_col{{{0}, + {8, 4}, + {8, 4}, + {4, 6, 2}, + {6, 2}, + {9, 3, 5}, + {9, 3, 5}, + {9, 3, 5, 1}, + {9, 3, 5, 1}, + {{0}, all_invalid}}, + all_valid}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT( + exec(make_collect_list_aggregation(null_policy::EXCLUDE))->view(), + lists_col{{{0}, + {8, 4}, + {8, 4}, + {4, 6, 2}, + {6, 2}, + {9, 3, 5}, + {9, 3, 5}, + {9, 3, 5, 1}, + {9, 3, 5, 1}, + {}}, + all_valid}); +} + +TYPED_TEST(TypedTimeRangeRollingTest, TimestampASC) +{ + // Confirm that timestamp columns can be used in range queries + // at all resolutions, given the right duration column type. + + using namespace cudf; + using TimeT = TypeParam; + using DurationT = cudf::detail::range_type; + using time_col = fwcw; + + // clang-format off + auto gby_column = int_col { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + auto agg_column = int_col {{0, 8, 4, 6, 2, 9, 3, 5, 1, 7}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}}; + auto time_column = time_col{ 1, 5, 6, 8, 9, 2, 2, 3, 4, 9}; + // clang-format on + + auto exec = + window_exec(gby_column, + time_column, + order::ASCENDING, + agg_column, + duration_scalar{DurationT{2}, true}, // 2 "durations" preceding. + duration_scalar{DurationT{1}, true}); // 1 "durations" following. + + verify_results_for_ascending(exec); +} + +template +void verify_results_for_descending(WindowExecT exec) +{ + auto const all_valid = thrust::make_constant_iterator(true); + auto const all_invalid = thrust::make_constant_iterator(false); + auto const first_invalid = thrust::make_transform_iterator(thrust::make_counting_iterator(0), + [](auto i) { return i != 0; }); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(exec(make_count_aggregation(null_policy::INCLUDE))->view(), + size_col{{1, 4, 4, 3, 3, 2, 3, 2, 2, 1}, all_valid}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(exec(make_count_aggregation())->view(), + size_col{{0, 4, 4, 3, 3, 2, 3, 2, 2, 1}, all_valid}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL( + exec(make_sum_aggregation())->view(), + fwcw{{1, 18, 18, 17, 17, 8, 12, 12, 12, 0}, first_invalid}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(exec(make_min_aggregation())->view(), + int_col{{1, 1, 1, 3, 3, 2, 2, 4, 4, 0}, first_invalid}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(exec(make_max_aggregation())->view(), + int_col{{1, 9, 9, 9, 9, 6, 6, 8, 8, 0}, first_invalid}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL( + exec(make_mean_aggregation())->view(), + fwcw{{1.0, 4.5, 4.5, 17.0 / 3, 17.0 / 3, 4.0, 4.0, 6.0, 6.0, 0.0}, first_invalid}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(exec(make_collect_list_aggregation())->view(), + lists_col{{{{0}, all_invalid}, + {1, 5, 3, 9}, + {1, 5, 3, 9}, + {5, 3, 9}, + {5, 3, 9}, + {2, 6}, + {2, 6, 4}, + {4, 8}, + {4, 8}, + {0}}, + all_valid}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT( + exec(make_collect_list_aggregation(null_policy::EXCLUDE))->view(), + lists_col{{{}, + {1, 5, 3, 9}, + {1, 5, 3, 9}, + {5, 3, 9}, + {5, 3, 9}, + {2, 6}, + {2, 6, 4}, + {4, 8}, + {4, 8}, + {0}}, + all_valid}); +} + +TYPED_TEST(TypedTimeRangeRollingTest, TimestampDESC) +{ + // Confirm that timestamp columns can be used in range queries + // at all resolutions, given the right duration column type. + using namespace cudf; + using TimeT = TypeParam; + using DurationT = cudf::detail::range_type; + using time_col = fwcw; + + // clang-format off + auto gby_column = int_col { 5, 5, 5, 5, 5, 1, 1, 1, 1, 1}; + auto agg_column = int_col {{7, 1, 5, 3, 9, 2, 6, 4, 8, 0}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; + auto time_column = time_col{ 9, 4, 3, 2, 2, 9, 8, 6, 5, 1}; + // clang-format on + + auto exec = + window_exec(gby_column, + time_column, + order::DESCENDING, + agg_column, + duration_scalar{DurationT{1}, true}, // 1 "durations" preceding. + duration_scalar{DurationT{2}, true}); // 2 "durations" following. + + verify_results_for_descending(exec); +} + +template +struct TypedIntegralRangeRollingTest : RangeRollingTest { +}; + +TYPED_TEST_CASE(TypedIntegralRangeRollingTest, cudf::test::IntegralTypesNotBool); + +TYPED_TEST(TypedIntegralRangeRollingTest, OrderByASC) +{ + // Confirm that integral ranges work with integral orderby columns, + // in ascending order. + using namespace cudf; + using T = TypeParam; + + // clang-format off + auto gby_column = int_col { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + auto agg_column = int_col {{0, 8, 4, 6, 2, 9, 3, 5, 1, 7}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}}; + auto oby_column = fwcw{ 1, 5, 6, 8, 9, 2, 2, 3, 4, 9}; + // clang-format on + + auto exec = window_exec(gby_column, + oby_column, + order::ASCENDING, + agg_column, + numeric_scalar(2), // 2 preceding. + numeric_scalar(1)); // 1 following. + + verify_results_for_ascending(exec); +} + +TYPED_TEST(TypedIntegralRangeRollingTest, OrderByDesc) +{ + // Confirm that integral ranges work with integral orderby columns, + // in descending order. + using namespace cudf; + using T = TypeParam; + + // clang-format off + auto gby_column = int_col { 5, 5, 5, 5, 5, 1, 1, 1, 1, 1}; + auto agg_column = int_col {{7, 1, 5, 3, 9, 2, 6, 4, 8, 0}, + {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; + auto oby_column = fwcw{ 9, 4, 3, 2, 2, 9, 8, 6, 5, 1}; + // clang-format on + + auto exec = window_exec(gby_column, + oby_column, + order::DESCENDING, + agg_column, + numeric_scalar(1), // 1 preceding. + numeric_scalar(2)); // 2 following. + + verify_results_for_descending(exec); +} + +template +struct TypedRangeRollingNullsTest : public RangeRollingTest { +}; + +using TypesUnderTest = IntegralTypesNotBool; + +TYPED_TEST_CASE(TypedRangeRollingNullsTest, TypesUnderTest); + +template +auto do_count_over_window( + cudf::column_view grouping_col, + cudf::column_view order_by, + cudf::order order, + cudf::column_view aggregation_col, + range_window_bounds&& preceding = range_window_bounds::get(numeric_scalar{T{1}, true}), + range_window_bounds&& following = range_window_bounds::get(numeric_scalar{T{1}, true})) +{ + auto const min_periods = size_type{1}; + auto const grouping_keys = cudf::table_view{std::vector{grouping_col}}; + + return cudf::grouped_range_rolling_window(grouping_keys, + order_by, + order, + aggregation_col, + std::move(preceding), + std::move(following), + min_periods, + cudf::make_count_aggregation()); +} + +TYPED_TEST(TypedRangeRollingNullsTest, CountSingleGroupOrderByASCNullsFirst) +{ + using T = TypeParam; + + // Groupby column. + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + // Aggregation column. + auto const agg_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 1, 1, 1, 1, 0, 1, 1, 1, 1}}; + // OrderBy column. + auto const oby_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}}; + + auto const output = do_count_over_window(grp_col, oby_col, cudf::order::ASCENDING, agg_col); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {4, 4, 4, 4, 1, 2, 2, 3, 3, 2}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, CountSingleGroupOrderByASCNullsLast) +{ + using namespace cudf::test; + using T = TypeParam; + + // Groupby column. + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + // Aggregation column. + auto const agg_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 1, 1, 1, 1, 0, 1, 1, 1, 1}}; + + // OrderBy column. + auto const oby_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0}}; + + auto const output = do_count_over_window(grp_col, oby_col, cudf::order::ASCENDING, agg_col); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {2, 3, 3, 3, 2, 1, 4, 4, 4, 4}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, CountMultiGroupOrderByASCNullsFirst) +{ + using namespace cudf::test; + using T = TypeParam; + + // Groupby column. + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + // Aggregation column. + auto const agg_col = fixed_width_column_wrapper{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + // OrderBy column. + auto const oby_col = + fixed_width_column_wrapper{{1, 2, 2, 1, 2, 1, 2, 3, 4, 5}, {0, 0, 0, 1, 1, 0, 0, 1, 1, 1}}; + + auto const output = do_count_over_window(grp_col, oby_col, cudf::order::ASCENDING, agg_col); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {3, 3, 3, 2, 2, 2, 2, 2, 3, 2}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, CountMultiGroupOrderByASCNullsLast) +{ + using namespace cudf::test; + using T = int32_t; + + // Groupby column. + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + // Aggregation column. + auto const agg_col = fixed_width_column_wrapper{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + // OrderBy column. + auto const oby_col = + fixed_width_column_wrapper{{1, 2, 2, 1, 3, 1, 2, 3, 4, 5}, {1, 1, 1, 0, 0, 1, 1, 1, 0, 0}}; + + auto const output = do_count_over_window(grp_col, oby_col, cudf::order::ASCENDING, agg_col); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {3, 3, 3, 2, 2, 2, 3, 2, 2, 2}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, CountSingleGroupOrderByDESCNullsFirst) +{ + using namespace cudf::test; + using T = TypeParam; + + // Groupby column. + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + // Aggregation column. + auto const agg_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 1, 1, 1, 1, 0, 1, 1, 1, 1}}; + // OrderBy column. + auto const oby_col = + fixed_width_column_wrapper{{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}}; + + auto const output = do_count_over_window(grp_col, oby_col, cudf::order::DESCENDING, agg_col); + ; + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {4, 4, 4, 4, 1, 2, 2, 3, 3, 2}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, CountSingleGroupOrderByDESCNullsLast) +{ + using namespace cudf::test; + using T = TypeParam; + + // Groupby column. + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + // Aggregation column. + auto const agg_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 1, 1, 1, 1, 0, 1, 1, 1, 1}}; + + // OrderBy column. + auto const oby_col = + fixed_width_column_wrapper{{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, {1, 1, 1, 1, 1, 1, 0, 0, 0, 0}}; + + auto const output = do_count_over_window(grp_col, oby_col, cudf::order::DESCENDING, agg_col); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {2, 3, 3, 3, 2, 1, 4, 4, 4, 4}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, CountMultiGroupOrderByDESCNullsFirst) +{ + using namespace cudf::test; + using T = TypeParam; + + // Groupby column. + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + // Aggregation column. + auto const agg_col = fixed_width_column_wrapper{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + // OrderBy column. + auto const oby_col = + fixed_width_column_wrapper{{4, 3, 2, 1, 0, 9, 8, 7, 6, 5}, {0, 0, 0, 1, 1, 0, 0, 1, 1, 1}}; + + auto const output = do_count_over_window(grp_col, oby_col, cudf::order::DESCENDING, agg_col); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {3, 3, 3, 2, 2, 2, 2, 2, 3, 2}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, CountMultiGroupOrderByDESCNullsLast) +{ + using namespace cudf::test; + using T = TypeParam; + + // Groupby column. + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + // Aggregation column. + auto const agg_col = fixed_width_column_wrapper{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + // OrderBy column. + auto const oby_col = + fixed_width_column_wrapper{{4, 3, 2, 1, 0, 9, 8, 7, 6, 5}, {1, 1, 1, 0, 0, 1, 1, 1, 0, 0}}; + + auto const output = do_count_over_window(grp_col, oby_col, cudf::order::DESCENDING, agg_col); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {2, 3, 2, 2, 2, 2, 3, 2, 2, 2}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, CountSingleGroupAllNullOrderBys) +{ + using namespace cudf::test; + using T = TypeParam; + + // Groupby column. + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + // Aggregation column. + auto const agg_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 1, 1, 1, 1, 0, 1, 1, 1, 1}}; + + // OrderBy column. + auto const oby_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + + auto const output = do_count_over_window(grp_col, oby_col, cudf::order::ASCENDING, agg_col); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, CountMultiGroupAllNullOrderBys) +{ + using namespace cudf::test; + using T = TypeParam; + + // Groupby column. + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + // Aggregation column. + auto const agg_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 1, 1, 1, 1, 0, 1, 1, 1, 1}}; + + // OrderBy column. + auto const oby_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 1, 1, 1, 1, 0, 0, 0, 0, 0}}; + + auto const output = do_count_over_window(grp_col, oby_col, cudf::order::ASCENDING, agg_col); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {2, 3, 3, 3, 2, 4, 4, 4, 4, 4}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, UnboundedPrecedingWindowSingleGroupOrderByASCNullsFirst) +{ + using namespace cudf::test; + using T = TypeParam; + + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + auto const agg_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 1, 1, 1, 1, 0, 1, 1, 1, 1}}; + auto const oby_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}}; + + auto const output = + do_count_over_window(grp_col, + oby_col, + cudf::order::ASCENDING, + agg_col, + range_window_bounds::unbounded(data_type{type_to_id()}), + range_window_bounds::get(numeric_scalar{1, true})); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {4, 4, 4, 4, 5, 6, 7, 8, 9, 9}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +TYPED_TEST(TypedRangeRollingNullsTest, UnboundedFollowingWindowSingleGroupOrderByASCNullsFirst) +{ + using namespace cudf::test; + using T = TypeParam; + + auto const grp_col = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + auto const agg_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 1, 1, 1, 1, 0, 1, 1, 1, 1}}; + auto const oby_col = + fixed_width_column_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}}; + + auto const output = + do_count_over_window(grp_col, + oby_col, + cudf::order::ASCENDING, + agg_col, + range_window_bounds::get(numeric_scalar{1, true}), + range_window_bounds::unbounded(data_type{type_to_id()})); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(output->view(), + fixed_width_column_wrapper{ + {9, 9, 9, 9, 5, 5, 4, 4, 3, 2}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}); +} + +} // namespace test +} // namespace cudf diff --git a/cpp/tests/rolling/range_window_bounds_test.cpp b/cpp/tests/rolling/range_window_bounds_test.cpp new file mode 100644 index 00000000000..aca40d6c0d1 --- /dev/null +++ b/cpp/tests/rolling/range_window_bounds_test.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +namespace cudf { +namespace test { + +struct RangeWindowBoundsTest : public BaseFixture { +}; + +template +struct TimestampRangeWindowBoundsTest : RangeWindowBoundsTest { +}; + +TYPED_TEST_CASE(TimestampRangeWindowBoundsTest, cudf::test::TimestampTypes); + +TEST_F(RangeWindowBoundsTest, TestBasicTimestampRangeTypeMapping) +{ + // Explicitly check that the programmatic mapping of orderby column types + // to their respective range and range_rep types is accurate. + + using namespace cudf::detail; + + static_assert(std::is_same, duration_D>::value); + static_assert(std::is_same, duration_s>::value); + static_assert(std::is_same, duration_ms>::value); + static_assert(std::is_same, duration_us>::value); + static_assert(std::is_same, duration_ns>::value); + + static_assert(std::is_same, int32_t>::value); + static_assert(std::is_same, int64_t>::value); + static_assert(std::is_same, int64_t>::value); + static_assert(std::is_same, int64_t>::value); + static_assert(std::is_same, int64_t>::value); +} + +TYPED_TEST(TimestampRangeWindowBoundsTest, BoundsConstruction) +{ + using OrderByType = TypeParam; + using range_type = cudf::detail::range_type; + using rep_type = cudf::detail::range_rep_type; + + using range_window_bounds = cudf::range_window_bounds; + + static_assert(cudf::is_duration()); + auto range_3 = range_window_bounds::get(duration_scalar{3, true}); + EXPECT_FALSE(range_3.is_unbounded() && + "range_window_bounds constructed from scalar cannot be unbounded."); + EXPECT_EQ(cudf::detail::range_comparable_value(range_3), rep_type{3}); + + auto range_unbounded = range_window_bounds::unbounded(data_type{type_to_id()}); + EXPECT_TRUE(range_unbounded.is_unbounded() && + "range_window_bounds::unbounded() must return an unbounded range."); + EXPECT_EQ(cudf::detail::range_comparable_value(range_unbounded), rep_type{}); +} + +TYPED_TEST(TimestampRangeWindowBoundsTest, WrongRangeType) +{ + using OrderByType = TypeParam; + + using wrong_range_type = + std::conditional_t::value, duration_ns, duration_D>; + auto range_3 = cudf::range_window_bounds::get(duration_scalar{3, true}); + + EXPECT_THROW(cudf::detail::range_comparable_value(range_3), cudf::logic_error); + + auto range_unbounded = range_window_bounds::unbounded(data_type{type_to_id()}); + EXPECT_THROW(cudf::detail::range_comparable_value(range_unbounded), + cudf::logic_error); +} + +template +struct NumericRangeWindowBoundsTest : RangeWindowBoundsTest { +}; + +using TypesForTest = cudf::test::IntegralTypesNotBool; + +TYPED_TEST_CASE(NumericRangeWindowBoundsTest, TypesForTest); + +TYPED_TEST(NumericRangeWindowBoundsTest, BasicNumericRangeTypeMapping) +{ + using T = TypeParam; + + using range_type = cudf::detail::range_type; + using range_rep_type = cudf::detail::range_rep_type; + + static_assert(std::is_same::value); + static_assert(std::is_same::value); +} + +TYPED_TEST(NumericRangeWindowBoundsTest, BoundsConstruction) +{ + using OrderByType = TypeParam; + using range_type = cudf::detail::range_type; + using rep_type = cudf::detail::range_rep_type; + + using range_window_bounds = cudf::range_window_bounds; + + static_assert(std::is_integral::value); + auto range_3 = range_window_bounds::get(numeric_scalar{3, true}); + EXPECT_FALSE(range_3.is_unbounded() && + "range_window_bounds constructed from scalar cannot be unbounded."); + EXPECT_EQ(cudf::detail::range_comparable_value(range_3), rep_type{3}); + + auto range_unbounded = range_window_bounds::unbounded(data_type{type_to_id()}); + EXPECT_TRUE(range_unbounded.is_unbounded() && + "range_window_bounds::unbounded() must return an unbounded range."); + EXPECT_EQ(cudf::detail::range_comparable_value(range_unbounded), rep_type{}); +} + +TYPED_TEST(NumericRangeWindowBoundsTest, WrongRangeType) +{ + using OrderByType = TypeParam; + + using wrong_range_type = + std::conditional_t::value, int16_t, int32_t>; + auto range_3 = cudf::range_window_bounds::get(numeric_scalar{3, true}); + + EXPECT_THROW(cudf::detail::range_comparable_value(range_3), cudf::logic_error); + + auto range_unbounded = range_window_bounds::unbounded(data_type{type_to_id()}); + EXPECT_THROW(cudf::detail::range_comparable_value(range_unbounded), + cudf::logic_error); +} + +} // namespace test +} // namespace cudf From 7f0ad1d8c9d1b797c6ead5917a3334d723c16bec Mon Sep 17 00:00:00 2001 From: Vyas Ramasubramani Date: Thu, 29 Apr 2021 07:07:04 -0700 Subject: [PATCH 02/65] Add support for pydocstyle and test on abc.py (#7985) This PR is related to #202 and adds support for using pydocstyle to lint docstrings. It adds flake8 configuration, a pre-commit hook, and sets up the flake8 config to enable incremental application of docstring styling. As an added bonus, because of the way that pydocstyle chooses not to lint docstrings on internal objects (those prefixed with an underscore), as more files are linted we can evaluate what constitutes public API for `cudf`. Once a file has been linted and is added to the regex filter in the config, ongoing CI checks will prevent regressions in the documentation. As an initial test, this PR lints the abc.py file containing the Serializable class (and updates the docstrings accordingly). We may want to mark that entire class as internal in any case, but it's important enough that it needs to be documented in any case (and perhaps we could settle on a standard docstring style for internal-facing code as well while we're at it). Authors: - Vyas Ramasubramani (https://github.com/vyasr) Approvers: - Christopher Harris (https://github.com/cwharris) - AJ Schmidt (https://github.com/ajschmidt8) - Keith Kraus (https://github.com/kkraus14) URL: https://github.com/rapidsai/cudf/pull/7985 --- .pre-commit-config.yaml | 6 ++ ci/checks/style.sh | 16 +++++- python/.flake8 | 8 ++- python/cudf/cudf/core/abc.py | 108 +++++++++++++++++++++++++++-------- 4 files changed, 112 insertions(+), 26 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 69f6634b5c2..f82fa9ef361 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,6 +41,12 @@ repos: entry: mypy --config-file=python/cudf/setup.cfg python/cudf/cudf language: system types: [python] + - repo: https://github.com/pycqa/pydocstyle + rev: 6.0.0 + hooks: + - id: pydocstyle + args: ["--config=python/.flake8"] + default_language_version: python: python3 diff --git a/ci/checks/style.sh b/ci/checks/style.sh index 17599c6d74d..981e886d31c 100755 --- a/ci/checks/style.sh +++ b/ci/checks/style.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2018, NVIDIA CORPORATION. +# Copyright (c) 2018-2021, NVIDIA CORPORATION. ##################### # cuDF Style Tester # ##################### @@ -33,6 +33,10 @@ FLAKE_CYTHON_RETVAL=$? MYPY_CUDF=`mypy --config=python/cudf/setup.cfg python/cudf/cudf` MYPY_CUDF_RETVAL=$? +# Run pydocstyle and get results/return code +PYDOCSTYLE=`pydocstyle --config=python/.flake8 python` +PYDOCSTYLE_RETVAL=$? + # Run clang-format and check for a consistent code format CLANG_FORMAT=`python cpp/scripts/run-clang-format.py 2>&1` CLANG_FORMAT_RETVAL=$? @@ -78,6 +82,14 @@ else echo -e "\n\n>>>> PASSED: mypy style check\n\n" fi +if [ "$PYDOCSTYLE_RETVAL" != "0" ]; then + echo -e "\n\n>>>> FAILED: pydocstyle style check; begin output\n\n" + echo -e "$PYDOCSTYLE" + echo -e "\n\n>>>> FAILED: pydocstyle style check; end output\n\n" +else + echo -e "\n\n>>>> PASSED: pydocstyle style check\n\n" +fi + if [ "$CLANG_FORMAT_RETVAL" != "0" ]; then echo -e "\n\n>>>> FAILED: clang format check; begin output\n\n" echo -e "$CLANG_FORMAT" @@ -91,7 +103,7 @@ HEADER_META=`ci/checks/headers_test.sh` HEADER_META_RETVAL=$? echo -e "$HEADER_META" -RETVALS=($ISORT_RETVAL $BLACK_RETVAL $FLAKE_RETVAL $FLAKE_CYTHON_RETVAL $CLANG_FORMAT_RETVAL $HEADER_META_RETVAL $MYPY_CUDF_RETVAL) +RETVALS=($ISORT_RETVAL $BLACK_RETVAL $FLAKE_RETVAL $FLAKE_CYTHON_RETVAL $PYDOCSTYLE_RETVAL $CLANG_FORMAT_RETVAL $HEADER_META_RETVAL $MYPY_CUDF_RETVAL) IFS=$'\n' RETVAL=`echo "${RETVALS[*]}" | sort -nr | head -n1` diff --git a/python/.flake8 b/python/.flake8 index 1d251eec69f..1ba1c9c644e 100644 --- a/python/.flake8 +++ b/python/.flake8 @@ -1,4 +1,4 @@ -# Copyright (c) 2020, NVIDIA CORPORATION. +# Copyright (c) 2020-2021, NVIDIA CORPORATION. [flake8] exclude = __init__.py @@ -8,3 +8,9 @@ ignore = # whitespace before : E203 +[pydocstyle] +match = ^.*abc\.py$ +# Due to https://github.com/PyCQA/pydocstyle/issues/363, we must exclude rather than include using match-dir. +match-dir = ^(?!ci|cpp|python/dask_cudf|python/cudf_kafka|python/custreamz).*$ +# In addition to numpy style, we additionally ignore magic methods (D105) and newlines before docstrings (D204). +add-ignore = D105, D204 diff --git a/python/cudf/cudf/core/abc.py b/python/cudf/cudf/core/abc.py index 0550b1d4de0..0439f0d24b8 100644 --- a/python/cudf/cudf/core/abc.py +++ b/python/cudf/cudf/core/abc.py @@ -1,4 +1,5 @@ -# Copyright (c) 2020, NVIDIA CORPORATION. +# Copyright (c) 2020-2021, NVIDIA CORPORATION. +"""Common abstract base classes for cudf.""" import abc import sys @@ -18,23 +19,78 @@ class Serializable(abc.ABC): + """A serializable object composed of device memory buffers. + + This base class defines a standard serialization protocol for objects + encapsulating device memory buffers. Serialization proceeds by copying + device data onto the host, then returning it along with suitable metadata + for reconstruction of the object. Deserialization performs the reverse + process, copying the serialized data from the host to new device buffers. + Subclasses must define the abstract methods :meth:`~.serialize` and + :meth:`~.deserialize`. The former defines the conversion of the object + into a representative collection of metadata and data buffers, while the + latter converts back from that representation into an equivalent object. + """ + @abstractmethod def serialize(self): + """Generate an equivalent serializable representation of an object. + + Subclasses must implement this method to define how the attributes of + the object are converted into a serializable representation. A common + solution is to construct a list containing device buffer attributes in + a well-defined order that can be reinterpreted upon deserialization, + then place all other lightweight attributes into the metadata + dictionary. + + Returns + ------- + Tuple[Dict, List] + The first element of the returned tuple is a dict containing any + serializable metadata required to reconstruct the object. The + second element is a list containing the device data buffers + or memoryviews of the object. + + :meta private: + """ pass @classmethod @abstractmethod def deserialize(cls, header, frames): + """Generate an object from a serialized representation. + + Subclasses must implement this method to define how objects of that + class can be constructed from a serialized representation generalized + by :meth:`serialize`. + + Parameters + ---------- + header : dict + The metadata required to reconstruct the object. + frames : list + The Buffers or memoryviews that the object should contain. + + Returns + ------- + Serializable + A new instance of `cls` (a subclass of `Serializable`) equivalent + to the instance that was serialized to produce the header and + frames. + + :meta private: + """ pass def device_serialize(self): - """Converts the object into a header and list of Buffer/memoryview - objects for file storage or network transmission. + """Serialize data and metadata associated with device memory. Returns ------- - header : dictionary containing any serializable metadata - frames : list of Buffer or memoryviews, commonly of length one + header : dict + The metadata required to reconstruct the object. + frames : list + The Buffers or memoryviews that the object should contain. :meta private: """ @@ -51,20 +107,24 @@ def device_serialize(self): @classmethod def device_deserialize(cls, header, frames): - """Convert serialized header and frames back - into respective Object Type + """Perform device-side deserialization tasks. + + The primary purpose of this method is the creation of device memory + buffers from host buffers where necessary. Parameters ---------- - cls : class of object header : dict - dictionary containing any serializable metadata - frames : list of Buffer or memoryview objects + The metadata required to reconstruct the object. + frames : list + The Buffers or memoryviews that the object should contain. Returns ------- - Deserialized Object of type cls extracted - from frames and header + Serializable + A new instance of `cls` (a subclass of `Serializable`) equivalent + to the instance that was serialized to produce the header and + frames. :meta private: """ @@ -84,13 +144,14 @@ def device_deserialize(cls, header, frames): return obj def host_serialize(self): - """Converts the object into a header and list of memoryview - objects for file storage or network transmission. + """Serialize data and metadata associated with host memory. Returns ------- - header : dictionary containing any serializable metadata - frames : list of memoryviews, commonly of length one + header : dict + The metadata required to reconstruct the object. + frames : list + The Buffers or memoryviews that the object should contain. :meta private: """ @@ -104,20 +165,21 @@ def host_serialize(self): @classmethod def host_deserialize(cls, header, frames): - """Convert serialized header and frames back - into respective Object Type + """Perform device-side deserialization tasks. Parameters ---------- - cls : class of object header : dict - dictionary containing any serializable metadata - frames : list of memoryview objects + The metadata required to reconstruct the object. + frames : list + The Buffers or memoryviews that the object should contain. Returns ------- - Deserialized Object of type cls extracted - from frames and header + Serializable + A new instance of `cls` (a subclass of `Serializable`) equivalent + to the instance that was serialized to produce the header and + frames. :meta private: """ From ac25e975d2f212ae67bc88296c6c5b4d2709bcff Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Thu, 29 Apr 2021 09:33:23 -0500 Subject: [PATCH 03/65] Add python/cython bindings for `str.join` API (#8085) Resolves #8079 This PR: - [x] Introduces bindings for `concatenate_list_elements` in cython and plumbs it to our python API, `.str.join` - [x] Enabled and adds more test coverage for `str.join`. - [x] Docstring addition and misc docs cleanup. Authors: - GALI PREM SAGAR (https://github.com/galipremsagar) Approvers: - Keith Kraus (https://github.com/kkraus14) URL: https://github.com/rapidsai/cudf/pull/8085 --- python/cudf/cudf/_lib/cpp/strings/combine.pxd | 13 +- python/cudf/cudf/_lib/strings/combine.pyx | 76 ++++++- python/cudf/cudf/core/column/string.py | 209 ++++++++++++++++-- python/cudf/cudf/tests/test_string.py | 122 +++++++++- 4 files changed, 401 insertions(+), 19 deletions(-) diff --git a/python/cudf/cudf/_lib/cpp/strings/combine.pxd b/python/cudf/cudf/_lib/cpp/strings/combine.pxd index 2670c67908f..250c6441882 100644 --- a/python/cudf/cudf/_lib/cpp/strings/combine.pxd +++ b/python/cudf/cudf/_lib/cpp/strings/combine.pxd @@ -1,4 +1,4 @@ -# Copyright (c) 2020, NVIDIA CORPORATION. +# Copyright (c) 2020-2021, NVIDIA CORPORATION. from cudf._lib.cpp.column.column_view cimport column_view from cudf._lib.cpp.table.table_view cimport table_view @@ -17,3 +17,14 @@ cdef extern from "cudf/strings/combine.hpp" namespace "cudf::strings" nogil: column_view source_strings, string_scalar separator, string_scalar narep) except + + + cdef unique_ptr[column] concatenate_list_elements( + column_view lists_strings_column, + column_view separators, + string_scalar separator_narep, + string_scalar string_narep) except + + + cdef unique_ptr[column] concatenate_list_elements( + column_view lists_strings_column, + string_scalar separator, + string_scalar narep) except + diff --git a/python/cudf/cudf/_lib/strings/combine.pyx b/python/cudf/cudf/_lib/strings/combine.pyx index 04fde5be9e8..25619de3ed0 100644 --- a/python/cudf/cudf/_lib/strings/combine.pyx +++ b/python/cudf/cudf/_lib/strings/combine.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2020, NVIDIA CORPORATION. +# Copyright (c) 2020-2021, NVIDIA CORPORATION. from libcpp.memory cimport unique_ptr from libcpp.utility cimport move @@ -15,7 +15,8 @@ from cudf._lib.table cimport Table from cudf._lib.cpp.strings.combine cimport ( concatenate as cpp_concatenate, - join_strings as cpp_join_strings + join_strings as cpp_join_strings, + concatenate_list_elements as cpp_concatenate_list_elements ) @@ -78,3 +79,74 @@ def join(Column source_strings, )) return Column.from_unique_ptr(move(c_result)) + + +def join_lists_with_scalar( + Column source_strings, + object py_separator, + object py_narep): + """ + Returns a Column by concatenating Lists of strings row-wise + in `source_strings` with the specified `py_separator` + between each string in lists and ``/`None` values + are replaced by `py_narep` + """ + + cdef DeviceScalar separator = py_separator.device_value + cdef DeviceScalar narep = py_narep.device_value + + cdef unique_ptr[column] c_result + cdef column_view source_view = source_strings.view() + + cdef const string_scalar* scalar_separator = \ + (separator.get_raw_ptr()) + cdef const string_scalar* scalar_narep = ( + narep.get_raw_ptr() + ) + + with nogil: + c_result = move(cpp_concatenate_list_elements( + source_view, + scalar_separator[0], + scalar_narep[0] + )) + + return Column.from_unique_ptr(move(c_result)) + + +def join_lists_with_column( + Column source_strings, + Column separator_strings, + object py_source_narep, + object py_separator_narep): + """ + Returns a Column by concatenating Lists of strings row-wise in + `source_strings` with a corresponding separator at the same + position in `separator_strings` and ``/`None` values in + `source_strings` are replaced by `py_source_narep` and + ``/`None` values in `separator_strings` are replaced + by `py_separator_narep` + """ + + cdef DeviceScalar source_narep = py_source_narep.device_value + cdef DeviceScalar separator_narep = py_separator_narep.device_value + + cdef unique_ptr[column] c_result + cdef column_view source_view = source_strings.view() + cdef column_view separator_view = separator_strings.view() + + cdef const string_scalar* scalar_source_narep = \ + (source_narep.get_raw_ptr()) + cdef const string_scalar* scalar_separator_narep = ( + separator_narep.get_raw_ptr() + ) + + with nogil: + c_result = move(cpp_concatenate_list_elements( + source_view, + separator_view, + scalar_separator_narep[0], + scalar_source_narep[0] + )) + + return Column.from_unique_ptr(move(c_result)) diff --git a/python/cudf/cudf/core/column/string.py b/python/cudf/cudf/core/column/string.py index ea919866e34..e4d7b6d4188 100644 --- a/python/cudf/cudf/core/column/string.py +++ b/python/cudf/cudf/core/column/string.py @@ -79,6 +79,8 @@ from cudf._lib.strings.combine import ( concatenate as cpp_concatenate, join as cpp_join, + join_lists_with_column as cpp_join_lists_with_column, + join_lists_with_scalar as cpp_join_lists_with_scalar, ) from cudf._lib.strings.contains import ( contains_re as cpp_contains_re, @@ -465,17 +467,196 @@ def cat(self, others=None, sep=None, na_rep=None): out = out[0] return out - def join(self, sep) -> ParentType: + def join( + self, sep=None, string_na_rep=None, sep_na_rep=None + ) -> ParentType: """ Join lists contained as elements in the Series/Index with passed delimiter. - Raises : NotImplementedError - Columns of arrays / lists are not yet supported. - """ - raise NotImplementedError( - "Columns of arrays / lists are not yet " "supported" + If the elements of a Series are lists themselves, join the content of + these lists using the delimiter passed to the function. + This function is an equivalent to :meth:`str.join`. + + Parameters + ---------- + sep : str or array-like + If str, the delimiter is used between list entries. + If array-like, the string at a position is used as a + delimiter for corresponding row of the list entries. + string_na_rep : str, default None + This character will take the place of any null strings + (not empty strings) in the Series. + If ``string_na_rep`` is ``None``, it defaults to empty + space "". + sep_na_rep : str, default None + This character will take the place of any null strings + (not empty strings) in `sep`. This parameter can be used + only if `sep` is array-like. If ``sep_na_rep`` is ``None``, + it defaults to empty space "". + + Returns + ------- + Series/Index: object + The list entries concatenated by intervening occurrences of + the delimiter. + + Raises + ------ + ValueError + - If ``sep_na_rep`` is supplied when ``sep`` is str. + - If ``sep`` is array-like and not of equal length with Series/Index. + TypeError + - If ``string_na_rep`` or ``sep_na_rep`` are not scalar values. + - If ``sep`` is not of following types: str or array-like. + + Examples + -------- + >>> import cudf + >>> ser = cudf.Series([['a', 'b', 'c'], ['d', 'e'], ['f'], ['g', ' ', 'h']]) + >>> ser + 0 [a, b, c] + 1 [d, e] + 2 [f] + 3 [g, , h] + dtype: list + >>> ser.str.join(sep='-') + 0 a-b-c + 1 d-e + 2 f + 3 g- -h + dtype: object + + ``sep`` can an array-like input: + + >>> ser.str.join(sep=['-', '+', '.', '=']) + 0 a-b-c + 1 d+e + 2 f + 3 g= =h + dtype: object + + If the actual series doesn't have lists, each character is joined + by `sep`: + + >>> ser = cudf.Series(['abc', 'def', 'ghi']) + >>> ser + 0 abc + 1 def + 2 ghi + dtype: object + >>> ser.str.join(sep='_') + 0 a_b_c + 1 d_e_f + 2 g_h_i + dtype: object + + We can replace ``/`None` values present in lists using + ``string_na_rep``: + + >>> ser = cudf.Series([['a', 'b', None], None, ['c', 'd']]) + >>> ser + 0 [a, b, None] + 1 None + 2 [c, d] + dtype: list + >>> ser.str.join(sep='_', string_na_rep='k') + 0 a_b_k + 1 + 2 c_d + dtype: object + + We can replace ``/`None` values present in lists of ``sep`` + using ``sep_na_rep``: + + >>> ser.str.join(sep=[None, '.', '-'], sep_na_rep='+') + 0 a+b+ + 1 + 2 c-d + dtype: object + """ # noqa E501 + if sep is None: + sep = "" + + if string_na_rep is None: + string_na_rep = "" + + if is_scalar(sep) and sep_na_rep: + raise ValueError( + "sep_na_rep cannot be defined when `sep` is scalar." + ) + + if sep_na_rep is None: + sep_na_rep = "" + + if not is_scalar(string_na_rep): + raise TypeError( + f"string_na_rep should be a string scalar, got {string_na_rep}" + f" of type : {type(string_na_rep)}" + ) + + if isinstance(self._column, cudf.core.column.ListColumn): + strings_column = self._column + else: + # If self._column is not a ListColumn, we will have to + # split each row by character and create a ListColumn out of it. + + # TODO: Remove this workaround after the following + # feature request is resolved + # FEA: https://github.com/rapidsai/cudf/issues/8094 + strings_column = self._split_by_character() + + if is_scalar(sep): + data = cpp_join_lists_with_scalar( + strings_column, cudf.Scalar(sep), cudf.Scalar(string_na_rep) + ) + elif can_convert_to_column(sep): + sep_column = column.as_column(sep) + if len(sep_column) != len(strings_column): + raise ValueError( + f"sep should be of similar size to the series, " + f"got: {len(sep_column)}, expected: {len(strings_column)}" + ) + if not is_scalar(sep_na_rep): + raise TypeError( + f"sep_na_rep should be a string scalar, got {sep_na_rep} " + f"of type: {type(sep_na_rep)}" + ) + + data = cpp_join_lists_with_column( + strings_column, + sep_column, + cudf.Scalar(string_na_rep), + cudf.Scalar(sep_na_rep), + ) + else: + raise TypeError( + f"sep should be an str, array-like or Series object, " + f"found {type(sep)}" + ) + + return self._return_or_inplace(data) + + def _split_by_character(self): + result_col = cpp_character_tokenize(self._column) + + bytes_count = cpp_count_bytes(self._column) + offset_col = cudf.core.column.column_empty( + row_count=len(bytes_count) + 1, dtype="int32" ) + offset_col[0] = 0 + offset_col[1:] = bytes_count + offset_col = offset_col._apply_scan_op("sum") + + res = cudf.core.column.ListColumn( + size=len(self._column), + dtype=cudf.ListDtype(self._column.dtype), + mask=self._column.mask, + offset=0, + null_count=self._column.null_count, + children=(offset_col, result_col), + ) + return res def extract( self, pat: str, flags: int = 0, expand: bool = True @@ -511,7 +692,7 @@ def extract( -------- >>> import cudf >>> s = cudf.Series(['a1', 'b2', 'c3']) - >>> s.str.extract(r'([ab])(\d)') # noqa W605 + >>> s.str.extract(r'([ab])(\d)') 0 1 0 a 1 1 b 2 @@ -520,7 +701,7 @@ def extract( A pattern with one group will return a DataFrame with one column if expand=True. - >>> s.str.extract(r'[ab](\d)', expand=True) # noqa W605 + >>> s.str.extract(r'[ab](\d)', expand=True) 0 0 1 1 2 @@ -528,12 +709,12 @@ def extract( A pattern with one group will return a Series if expand=False. - >>> s.str.extract(r'[ab](\d)', expand=False) # noqa W605 + >>> s.str.extract(r'[ab](\d)', expand=False) 0 1 1 2 2 dtype: object - """ + """ # noqa W605 if flags != 0: raise NotImplementedError("`flags` parameter is not yet supported") @@ -621,7 +802,7 @@ def contains( Returning any digit using regular expression. - >>> s1.str.contains('\d', regex=True) # noqa W605 + >>> s1.str.contains('\d', regex=True) 0 False 1 False 2 False @@ -654,7 +835,7 @@ def contains( 3 True 4 dtype: bool - """ + """ # noqa W605 if case is not True: raise NotImplementedError("`case` parameter is not yet supported") elif flags != 0: @@ -3075,7 +3256,7 @@ def count(self, pat: str, flags: int = 0) -> ParentType: Escape ``'$'`` to find the literal dollar sign. >>> s = cudf.Series(['$', 'B', 'Aab$', '$$ca', 'C$B$', 'cat']) - >>> s.str.count('\$') # noqa W605 + >>> s.str.count('\$') 0 1 1 0 2 1 @@ -3089,7 +3270,7 @@ def count(self, pat: str, flags: int = 0) -> ParentType: >>> index = cudf.core.index.StringIndex(['A', 'A', 'Aaba', 'cat']) >>> index.str.count('a') Int64Index([0, 0, 2, 1], dtype='int64') - """ + """ # noqa W605 if flags != 0: raise NotImplementedError("`flags` parameter is not yet supported") diff --git a/python/cudf/cudf/tests/test_string.py b/python/cudf/cudf/tests/test_string.py index 2ca6bc622be..0ff5b81ce81 100644 --- a/python/cudf/cudf/tests/test_string.py +++ b/python/cudf/cudf/tests/test_string.py @@ -807,8 +807,7 @@ def test_string_cat_str_error(): gs.str.cat(gs.str) -@pytest.mark.xfail(raises=(NotImplementedError, AttributeError)) -@pytest.mark.parametrize("sep", [None, "", " ", "|", ",", "|||"]) +@pytest.mark.parametrize("sep", ["", " ", "|", ",", "|||"]) def test_string_join(ps_gs, sep): ps, gs = ps_gs @@ -2931,3 +2930,122 @@ def test_string_slice_with_mask(): assert_eq(actual._column.null_count, expected._column.null_count) assert_eq(actual, expected) + + +def test_str_join_lists_error(): + sr = cudf.Series([["a", "a"], ["b"], ["c"]]) + + with pytest.raises( + ValueError, match="sep_na_rep cannot be defined when `sep` is scalar." + ): + sr.str.join(sep="-", sep_na_rep="-") + + with pytest.raises( + TypeError, + match=re.escape( + "string_na_rep should be a string scalar, got [10, 20] of type " + ": " + ), + ): + sr.str.join(string_na_rep=[10, 20]) + + with pytest.raises( + ValueError, + match=re.escape( + "sep should be of similar size to the series, got: 2, expected: 3" + ), + ): + sr.str.join(sep=["=", "-"]) + + with pytest.raises( + TypeError, + match=re.escape( + "sep_na_rep should be a string scalar, got " + "['na'] of type: " + ), + ): + sr.str.join(sep=["-", "+", "."], sep_na_rep=["na"]) + + with pytest.raises( + TypeError, + match=re.escape( + "sep should be an str, array-like or Series object, " + "found " + ), + ): + sr.str.join(sep=cudf.DataFrame()) + + +@pytest.mark.parametrize( + "sr,sep,string_na_rep,sep_na_rep,expected", + [ + ( + cudf.Series([["a", "a"], ["b"], ["c"]]), + "-", + None, + None, + cudf.Series(["a-a", "b", "c"]), + ), + ( + cudf.Series([["a", "b"], [None], [None, "hello", None, "world"]]), + "__", + "=", + None, + cudf.Series(["a__b", "=", "=__hello__=__world"]), + ), + ( + cudf.Series( + [ + ["a", None, "b"], + [None], + [None, "hello", None, "world"], + None, + ] + ), + ["-", "_", "**", "!"], + None, + None, + cudf.Series(["a--b", "", "**hello****world", None]), + ), + ( + cudf.Series( + [ + ["a", None, "b"], + [None], + [None, "hello", None, "world"], + None, + ] + ), + ["-", "_", "**", None], + "rep_str", + "sep_str", + cudf.Series( + [ + "a-rep_str-b", + "rep_str", + "rep_str**hello**rep_str**world", + None, + ] + ), + ), + ( + cudf.Series([[None, "a"], [None], None]), + ["-", "_", None], + "rep_str", + None, + cudf.Series(["rep_str-a", "rep_str", None]), + ), + ( + cudf.Series([[None, "a"], [None], None]), + ["-", "_", None], + None, + "sep_str", + cudf.Series(["-a", "", None]), + ), + ], +) +def test_str_join_lists(sr, sep, string_na_rep, sep_na_rep, expected): + actual = sr.str.join( + sep=sep, string_na_rep=string_na_rep, sep_na_rep=sep_na_rep + ) + assert_eq(actual, expected) From ac4f94342febb68e23197881d502d2c9ce8833e0 Mon Sep 17 00:00:00 2001 From: Mark Harris Date: Fri, 30 Apr 2021 03:49:37 +1000 Subject: [PATCH 04/65] Convert hashing, partitioning, and nested_loop_join APIs to use device_uvector (#8074) Converts internals of cudf::partition/hash_partition, hashing, and nested_loop_join to use device_uvector instead of device_vector. Changes `cudf::hash` API to take a `host_span` instead of a `std::vector`, hence marked as a breaking change. Contributes to #7287 Performance is improved in hash_partition for smaller sizes, and is similar for larger sizes. ``` (rapids) rapids@compose:~/cudf/cpp/build/release$ _deps/benchmark-src/tools/compare.py benchmarks ~/cudf/cpp/build/hashing_before.json ~/cudf/cpp/build/hashing_after.json Comparing /home/mharris/rapids/cudf/cpp/build/hashing_before.json to /home/mharris/rapids/cudf/cpp/build/hashing_after.json Benchmark Time CPU Time Old Time New CPU Old CPU New -------------------------------------------------------------------------------------------------------------------------------------------------- Hashing/hash_partition/131072/1/64/manual_time -0.2817 -0.2422 0 0 0 0 Hashing/hash_partition/262144/1/64/manual_time -0.2561 -0.2208 0 0 0 0 Hashing/hash_partition/524288/1/64/manual_time -0.2269 -0.1992 0 0 0 0 Hashing/hash_partition/1048576/1/64/manual_time -0.1864 -0.1683 0 0 0 0 Hashing/hash_partition/2097152/1/64/manual_time -0.1394 -0.1323 0 0 0 0 Hashing/hash_partition/131072/1/128/manual_time -0.2732 -0.2340 0 0 0 0 Hashing/hash_partition/262144/1/128/manual_time -0.2561 -0.2218 0 0 0 0 Hashing/hash_partition/524288/1/128/manual_time -0.2223 -0.1946 0 0 0 0 Hashing/hash_partition/1048576/1/128/manual_time -0.1847 -0.1664 0 0 0 0 Hashing/hash_partition/2097152/1/128/manual_time -0.1345 -0.1260 0 0 0 0 Hashing/hash_partition/131072/1/256/manual_time -0.2626 -0.2280 0 0 0 0 Hashing/hash_partition/262144/1/256/manual_time -0.2545 -0.2206 0 0 0 0 Hashing/hash_partition/524288/1/256/manual_time -0.2178 -0.1918 0 0 0 0 Hashing/hash_partition/1048576/1/256/manual_time -0.1739 -0.1577 0 0 0 0 Hashing/hash_partition/2097152/1/256/manual_time -0.1389 -0.1316 0 0 0 0 Hashing/hash_partition/131072/1/512/manual_time -0.2492 -0.2171 0 0 0 0 Hashing/hash_partition/262144/1/512/manual_time -0.2383 -0.2060 0 0 0 0 Hashing/hash_partition/524288/1/512/manual_time -0.1971 -0.1758 0 0 0 0 Hashing/hash_partition/1048576/1/512/manual_time -0.1679 -0.1529 0 0 0 0 Hashing/hash_partition/2097152/1/512/manual_time -0.1426 -0.1394 0 0 0 0 Hashing/hash_partition/131072/1/1024/manual_time -0.2246 -0.1952 0 0 0 0 Hashing/hash_partition/262144/1/1024/manual_time -0.2153 -0.1880 0 0 0 0 Hashing/hash_partition/524288/1/1024/manual_time -0.1851 -0.1664 0 0 0 0 Hashing/hash_partition/1048576/1/1024/manual_time -0.1456 -0.1350 0 0 0 0 Hashing/hash_partition/2097152/1/1024/manual_time -0.1190 -0.1141 0 0 0 0 Hashing/hash_partition/131072/16/64/manual_time -0.0639 -0.0638 0 0 0 0 Hashing/hash_partition/262144/16/64/manual_time -0.0462 -0.0450 0 0 0 0 Hashing/hash_partition/524288/16/64/manual_time -0.0354 -0.0368 1 1 1 1 Hashing/hash_partition/1048576/16/64/manual_time -0.0152 -0.0182 1 1 1 1 Hashing/hash_partition/2097152/16/64/manual_time -0.0142 -0.0151 2 2 2 2 Hashing/hash_partition/131072/16/128/manual_time -0.0618 -0.0604 0 0 0 0 Hashing/hash_partition/262144/16/128/manual_time -0.0472 -0.0463 0 0 0 0 Hashing/hash_partition/524288/16/128/manual_time -0.0383 -0.0383 1 1 1 1 Hashing/hash_partition/1048576/16/128/manual_time -0.0172 -0.0173 1 1 1 1 Hashing/hash_partition/2097152/16/128/manual_time -0.0034 -0.0037 2 2 2 2 Hashing/hash_partition/131072/16/256/manual_time -0.0542 -0.0539 0 0 0 0 Hashing/hash_partition/262144/16/256/manual_time -0.0472 -0.0463 0 0 0 0 Hashing/hash_partition/524288/16/256/manual_time -0.0382 -0.0391 1 1 1 1 Hashing/hash_partition/1048576/16/256/manual_time -0.0304 -0.0306 1 1 1 1 Hashing/hash_partition/2097152/16/256/manual_time -0.0178 -0.0187 2 2 2 2 Hashing/hash_partition/131072/16/512/manual_time +0.0108 +0.0198 0 0 0 0 Hashing/hash_partition/262144/16/512/manual_time -0.0391 -0.0391 1 0 1 1 Hashing/hash_partition/524288/16/512/manual_time -0.0310 -0.0311 1 1 1 1 Hashing/hash_partition/1048576/16/512/manual_time -0.0273 -0.0372 1 1 1 1 Hashing/hash_partition/2097152/16/512/manual_time -0.0043 -0.0050 2 2 2 2 Hashing/hash_partition/131072/16/1024/manual_time -0.0426 -0.0435 1 1 1 1 Hashing/hash_partition/262144/16/1024/manual_time -0.0318 -0.0319 1 1 1 1 Hashing/hash_partition/524288/16/1024/manual_time -0.0267 -0.0273 1 1 1 1 Hashing/hash_partition/1048576/16/1024/manual_time -0.0169 -0.0179 2 2 2 2 Hashing/hash_partition/2097152/16/1024/manual_time -0.0120 -0.0127 3 3 3 3 Hashing/hash_partition/131072/256/64/manual_time +0.0106 +0.0098 4 4 4 4 Hashing/hash_partition/262144/256/64/manual_time +0.0084 +0.0073 6 6 6 6 Hashing/hash_partition/524288/256/64/manual_time +0.0054 +0.0055 9 9 9 9 Hashing/hash_partition/1048576/256/64/manual_time +0.0160 +0.0154 17 17 17 17 Hashing/hash_partition/2097152/256/64/manual_time -0.0066 -0.0067 33 32 33 32 Hashing/hash_partition/131072/256/128/manual_time +0.0121 +0.0118 4 4 4 4 Hashing/hash_partition/262144/256/128/manual_time +0.0071 +0.0068 6 6 6 6 Hashing/hash_partition/524288/256/128/manual_time +0.0042 +0.0041 9 9 9 9 Hashing/hash_partition/1048576/256/128/manual_time +0.0039 +0.0039 17 17 17 17 Hashing/hash_partition/2097152/256/128/manual_time +0.0074 +0.0073 33 33 33 33 Hashing/hash_partition/131072/256/256/manual_time +0.0276 +0.0264 5 5 5 5 Hashing/hash_partition/262144/256/256/manual_time +0.0072 +0.0068 6 6 6 6 Hashing/hash_partition/524288/256/256/manual_time +0.0022 +0.0020 9 9 9 9 Hashing/hash_partition/1048576/256/256/manual_time -0.0035 -0.0036 17 17 17 17 Hashing/hash_partition/2097152/256/256/manual_time -0.0042 -0.0042 34 34 34 34 Hashing/hash_partition/131072/256/512/manual_time -0.0526 -0.0588 6 6 6 6 Hashing/hash_partition/262144/256/512/manual_time +0.0065 +0.0059 7 7 7 7 Hashing/hash_partition/524288/256/512/manual_time +0.0058 +0.0054 11 11 11 11 Hashing/hash_partition/1048576/256/512/manual_time -0.0030 -0.0030 20 20 20 20 Hashing/hash_partition/2097152/256/512/manual_time +0.0042 +0.0041 37 37 37 37 Hashing/hash_partition/131072/256/1024/manual_time +0.0075 +0.0066 7 8 7 8 Hashing/hash_partition/262144/256/1024/manual_time +0.0033 +0.0025 9 9 9 9 Hashing/hash_partition/524288/256/1024/manual_time +0.0025 +0.0019 14 14 14 14 Hashing/hash_partition/2097152/256/1024/manual_time +0.0046 +0.0045 46 46 46 46 ``` Authors: - Mark Harris (https://github.com/harrism) Approvers: - Vukasin Milovanovic (https://github.com/vuule) - Nghia Truong (https://github.com/ttnghia) URL: https://github.com/rapidsai/cudf/pull/8074 --- cpp/include/cudf/detail/hashing.hpp | 18 +-- cpp/include/cudf/hashing.hpp | 15 +-- cpp/include/cudf/lists/detail/scatter.cuh | 8 +- cpp/src/hash/hashing.cu | 23 ++-- cpp/src/join/nested_loop_join.cuh | 25 ++-- cpp/src/partitioning/partitioning.cu | 114 +++++++++--------- cpp/src/partitioning/round_robin.cu | 47 ++------ .../partitioning/hash_partition_test.cpp | 9 +- 8 files changed, 115 insertions(+), 144 deletions(-) diff --git a/cpp/include/cudf/detail/hashing.hpp b/cpp/include/cudf/detail/hashing.hpp index 06f523c2320..83d6be14709 100644 --- a/cpp/include/cudf/detail/hashing.hpp +++ b/cpp/include/cudf/detail/hashing.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,17 +29,17 @@ namespace detail { */ std::unique_ptr hash( table_view const& input, - hash_id hash_function = hash_id::HASH_MURMUR3, - std::vector const& initial_hash = {}, - uint32_t seed = 0, - rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + hash_id hash_function = hash_id::HASH_MURMUR3, + cudf::host_span initial_hash = {}, + uint32_t seed = 0, + rmm::cuda_stream_view stream = rmm::cuda_stream_default, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); std::unique_ptr murmur_hash3_32( table_view const& input, - std::vector const& initial_hash = {}, - rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + cudf::host_span initial_hash = {}, + rmm::cuda_stream_view stream = rmm::cuda_stream_default, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); std::unique_ptr md5_hash( table_view const& input, diff --git a/cpp/include/cudf/hashing.hpp b/cpp/include/cudf/hashing.hpp index 0fb5002a953..73bff0b36e5 100644 --- a/cpp/include/cudf/hashing.hpp +++ b/cpp/include/cudf/hashing.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ #include #include +#include namespace cudf { /** @@ -29,18 +30,18 @@ namespace cudf { * @brief Computes the hash value of each row in the input set of columns. * * @param input The table of columns to hash - * @param initial_hash Optional vector of initial hash values for each column. - * If this vector is empty then each element will be hashed as-is. + * @param initial_hash Optional host_span of initial hash values for each column. + * If this span is empty then each element will be hashed as-is. * @param mr Device memory resource used to allocate the returned column's device memory. * * @returns A column where each row is the hash of a column from the input */ std::unique_ptr hash( table_view const& input, - hash_id hash_function = hash_id::HASH_MURMUR3, - std::vector const& initial_hash = {}, - uint32_t seed = DEFAULT_HASH_SEED, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + hash_id hash_function = hash_id::HASH_MURMUR3, + cudf::host_span initial_hash = {}, + uint32_t seed = DEFAULT_HASH_SEED, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** @} */ // end of group } // namespace cudf diff --git a/cpp/include/cudf/lists/detail/scatter.cuh b/cpp/include/cudf/lists/detail/scatter.cuh index 8e2ecdf49a7..1de35b95488 100644 --- a/cpp/include/cudf/lists/detail/scatter.cuh +++ b/cpp/include/cudf/lists/detail/scatter.cuh @@ -46,8 +46,8 @@ namespace { * also holding a reference to the list column. * * Analogous to the list_view, this class is default constructable, - * and can thus be stored in rmm::device_vector. It is used to represent - * the results of a `scatter()` operation; a device_vector may hold + * and can thus be stored in rmm::device_uvector. It is used to represent + * the results of a `scatter()` operation; a device_uvector may hold * several instances of unbound_list_view, each with a flag indicating * whether it came from the scatter source or target. Each instance * may later be "bound" to the appropriate source/target column, to @@ -131,7 +131,7 @@ struct unbound_list_view { } private: - // Note: Cannot store reference to list column, because of storage in device_vector. + // Note: Cannot store reference to list column, because of storage in device_uvector. // Only keep track of whether this list row came from the source or target of scatter. label_type _label{ @@ -247,7 +247,7 @@ void print(std::string const& msg, * The protocol is as follows: * * Inputs: - * 1. list_vector: A device_vector of unbound_list_view, with each element + * 1. list_vector: A device_uvector of unbound_list_view, with each element * indicating the position, size, and which column the list * row came from. * 2. list_offsets: The offsets column for the (outer) lists column, each offset diff --git a/cpp/src/hash/hashing.cu b/cpp/src/hash/hashing.cu index 530849601de..2cfcd1dbcc8 100644 --- a/cpp/src/hash/hashing.cu +++ b/cpp/src/hash/hashing.cu @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -62,7 +63,7 @@ namespace detail { std::unique_ptr hash(table_view const& input, hash_id hash_function, - std::vector const& initial_hash, + cudf::host_span initial_hash, uint32_t seed, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) @@ -197,7 +198,7 @@ std::unique_ptr serial_murmur_hash3_32(table_view const& input, } std::unique_ptr murmur_hash3_32(table_view const& input, - std::vector const& initial_hash, + cudf::host_span initial_hash, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { @@ -216,20 +217,20 @@ std::unique_ptr murmur_hash3_32(table_view const& input, if (!initial_hash.empty()) { CUDF_EXPECTS(initial_hash.size() == size_t(input.num_columns()), "Expected same size of initial hash values as number of columns"); - auto device_initial_hash = rmm::device_vector(initial_hash); + auto device_initial_hash = make_device_uvector_async(initial_hash, stream); if (nullable) { - thrust::tabulate(rmm::exec_policy(stream), - output_view.begin(), - output_view.end(), - row_hasher_initial_values( - *device_input, device_initial_hash.data().get())); + thrust::tabulate( + rmm::exec_policy(stream), + output_view.begin(), + output_view.end(), + row_hasher_initial_values(*device_input, device_initial_hash.data())); } else { thrust::tabulate(rmm::exec_policy(stream), output_view.begin(), output_view.end(), row_hasher_initial_values( - *device_input, device_initial_hash.data().get())); + *device_input, device_initial_hash.data())); } } else { if (nullable) { @@ -252,7 +253,7 @@ std::unique_ptr murmur_hash3_32(table_view const& input, std::unique_ptr hash(table_view const& input, hash_id hash_function, - std::vector const& initial_hash, + cudf::host_span initial_hash, uint32_t seed, rmm::mr::device_memory_resource* mr) { @@ -261,7 +262,7 @@ std::unique_ptr hash(table_view const& input, } std::unique_ptr murmur_hash3_32(table_view const& input, - std::vector const& initial_hash, + cudf::host_span initial_hash, rmm::mr::device_memory_resource* mr) { CUDF_FUNC_RANGE(); diff --git a/cpp/src/join/nested_loop_join.cuh b/cpp/src/join/nested_loop_join.cuh index 580017a6704..5054305a41a 100644 --- a/cpp/src/join/nested_loop_join.cuh +++ b/cpp/src/join/nested_loop_join.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -118,7 +118,7 @@ size_type estimate_nested_loop_join_output_size(table_device_view left, * * @return Join output indices vector pair */ -std::pair, rmm::device_vector> +std::pair, rmm::device_uvector> get_base_nested_loop_join_indices(table_view const& left, table_view const& right, bool flip_join_indices, @@ -144,7 +144,8 @@ get_base_nested_loop_join_indices(table_view const& left, // If the estimated output size is zero, return immediately if (estimated_size == 0) { - return std::make_pair(rmm::device_vector{}, rmm::device_vector{}); + return std::make_pair(rmm::device_uvector{0, stream}, + rmm::device_uvector{0, stream}); } // Because we are approximating the number of joined elements, our approximation @@ -154,22 +155,20 @@ get_base_nested_loop_join_indices(table_view const& left, rmm::device_scalar write_index(0, stream); size_type join_size{0}; - rmm::device_vector left_indices; - rmm::device_vector right_indices; + rmm::device_uvector left_indices{0, stream}; + rmm::device_uvector right_indices{0, stream}; auto current_estimated_size = estimated_size; do { - left_indices.resize(estimated_size); - right_indices.resize(estimated_size); + left_indices.resize(estimated_size, stream); + right_indices.resize(estimated_size, stream); constexpr int block_size{DEFAULT_JOIN_BLOCK_SIZE}; detail::grid_1d config(left_table->num_rows(), block_size); write_index.set_value_zero(stream); row_equality equality{*left_table, *right_table, compare_nulls == null_equality::EQUAL}; - const auto& join_output_l = - flip_join_indices ? right_indices.data().get() : left_indices.data().get(); - const auto& join_output_r = - flip_join_indices ? left_indices.data().get() : right_indices.data().get(); + const auto& join_output_l = flip_join_indices ? right_indices.data() : left_indices.data(); + const auto& join_output_r = flip_join_indices ? left_indices.data() : right_indices.data(); nested_loop_join <<>>(*left_table, *right_table, @@ -187,8 +186,8 @@ get_base_nested_loop_join_indices(table_view const& left, estimated_size *= 2; } while ((current_estimated_size < join_size)); - left_indices.resize(join_size); - right_indices.resize(join_size); + left_indices.resize(join_size, stream); + right_indices.resize(join_size, stream); return std::make_pair(std::move(left_indices), std::move(right_indices)); } diff --git a/cpp/src/partitioning/partitioning.cu b/cpp/src/partitioning/partitioning.cu index 209e2d16f87..5401b3981b6 100644 --- a/cpp/src/partitioning/partitioning.cu +++ b/cpp/src/partitioning/partitioning.cu @@ -22,11 +22,13 @@ #include #include #include +#include #include #include #include #include +#include #include namespace cudf { @@ -355,20 +357,20 @@ void copy_block_partitions_impl(InputIter const input, scanned_block_partition_sizes); } -rmm::device_vector compute_gather_map(size_type num_rows, - size_type num_partitions, - size_type const* row_partition_numbers, - size_type const* row_partition_offset, - size_type const* block_partition_sizes, - size_type const* scanned_block_partition_sizes, - size_type grid_size, - rmm::cuda_stream_view stream) +rmm::device_uvector compute_gather_map(size_type num_rows, + size_type num_partitions, + size_type const* row_partition_numbers, + size_type const* row_partition_offset, + size_type const* block_partition_sizes, + size_type const* scanned_block_partition_sizes, + size_type grid_size, + rmm::cuda_stream_view stream) { auto sequence = thrust::make_counting_iterator(0); - rmm::device_vector gather_map(num_rows); + rmm::device_uvector gather_map(num_rows, stream); copy_block_partitions_impl(sequence, - gather_map.data().get(), + gather_map.begin(), num_rows, num_partitions, row_partition_numbers, @@ -464,7 +466,7 @@ std::pair, std::vector> hash_partition_table( auto grid_size = util::div_rounding_up_safe(num_rows, rows_per_block); // Allocate array to hold which partition each row belongs to - auto row_partition_numbers = rmm::device_vector(num_rows); + auto row_partition_numbers = rmm::device_uvector(num_rows, stream); // Array to hold the size of each partition computed by each block // i.e., { {block0 partition0 size, block1 partition0 size, ...}, @@ -472,14 +474,17 @@ std::pair, std::vector> hash_partition_table( // ... // {block0 partition(num_partitions-1) size, block1 // partition(num_partitions -1) size, ...} } - auto block_partition_sizes = rmm::device_vector(grid_size * num_partitions); + auto block_partition_sizes = rmm::device_uvector(grid_size * num_partitions, stream); - auto scanned_block_partition_sizes = rmm::device_vector(grid_size * num_partitions); + auto scanned_block_partition_sizes = + rmm::device_uvector(grid_size * num_partitions, stream); // Holds the total number of rows in each partition - auto global_partition_sizes = rmm::device_vector(num_partitions, size_type{0}); + auto global_partition_sizes = + cudf::detail::make_zeroed_device_uvector_async(num_partitions, stream); - auto row_partition_offset = rmm::device_vector(num_rows); + auto row_partition_offset = + cudf::detail::make_zeroed_device_uvector_async(num_rows, stream); auto const device_input = table_device_view::create(table_to_hash, stream); auto const hasher = row_hasher(*device_input, seed); @@ -502,10 +507,10 @@ std::pair, std::vector> hash_partition_table( num_rows, num_partitions, partitioner_type(num_partitions), - row_partition_numbers.data().get(), - row_partition_offset.data().get(), - block_partition_sizes.data().get(), - global_partition_sizes.data().get()); + row_partition_numbers.data(), + row_partition_offset.data(), + block_partition_sizes.data(), + global_partition_sizes.data()); } else { // Determines how the mapping between hash value and partition number is // computed @@ -522,10 +527,10 @@ std::pair, std::vector> hash_partition_table( num_rows, num_partitions, partitioner_type(num_partitions), - row_partition_numbers.data().get(), - row_partition_offset.data().get(), - block_partition_sizes.data().get(), - global_partition_sizes.data().get()); + row_partition_numbers.data(), + row_partition_offset.data(), + block_partition_sizes.data(), + global_partition_sizes.data()); } // Compute exclusive scan of all blocks' partition sizes in-place to determine @@ -533,25 +538,20 @@ std::pair, std::vector> hash_partition_table( thrust::exclusive_scan(rmm::exec_policy(stream), block_partition_sizes.begin(), block_partition_sizes.end(), - scanned_block_partition_sizes.data().get()); + scanned_block_partition_sizes.data()); // Compute exclusive scan of size of each partition to determine offset // location of each partition in final output. // TODO This can be done independently on a separate stream - size_type* scanned_global_partition_sizes{global_partition_sizes.data().get()}; thrust::exclusive_scan(rmm::exec_policy(stream), global_partition_sizes.begin(), global_partition_sizes.end(), - scanned_global_partition_sizes); + global_partition_sizes.begin()); // Copy the result of the exclusive scan to the output offsets array // to indicate the starting point for each partition in the output - std::vector partition_offsets(num_partitions); - CUDA_TRY(cudaMemcpyAsync(partition_offsets.data(), - scanned_global_partition_sizes, - num_partitions * sizeof(size_type), - cudaMemcpyDeviceToHost, - stream.value())); + auto const partition_offsets = + cudf::detail::make_std_vector_async(global_partition_sizes, stream); // When the number of partitions is less than a threshold, we can apply an // optimization using shared memory to copy values to the output buffer. @@ -559,23 +559,16 @@ std::pair, std::vector> hash_partition_table( if (use_optimization) { std::vector> output_cols(input.num_columns()); - // NOTE these pointers are non-const to workaround lambda capture bug in - // gcc 5.4 - auto row_partition_numbers_ptr{row_partition_numbers.data().get()}; - auto row_partition_offset_ptr{row_partition_offset.data().get()}; - auto block_partition_sizes_ptr{block_partition_sizes.data().get()}; - auto scanned_block_partition_sizes_ptr{scanned_block_partition_sizes.data().get()}; - // Copy input to output by partition per column - std::transform(input.begin(), input.end(), output_cols.begin(), [=](auto const& col) { + std::transform(input.begin(), input.end(), output_cols.begin(), [&](auto const& col) { return cudf::type_dispatcher(col.type(), copy_block_partitions_dispatcher{}, col, num_partitions, - row_partition_numbers_ptr, - row_partition_offset_ptr, - block_partition_sizes_ptr, - scanned_block_partition_sizes_ptr, + row_partition_numbers.data(), + row_partition_offset.data(), + block_partition_sizes.data(), + scanned_block_partition_sizes.data(), grid_size, stream, mr); @@ -585,10 +578,10 @@ std::pair, std::vector> hash_partition_table( // Use copy_block_partitions to compute a gather map auto gather_map = compute_gather_map(num_rows, num_partitions, - row_partition_numbers_ptr, - row_partition_offset_ptr, - block_partition_sizes_ptr, - scanned_block_partition_sizes_ptr, + row_partition_numbers.data(), + row_partition_offset.data(), + block_partition_sizes.data(), + scanned_block_partition_sizes.data(), grid_size, stream); @@ -597,13 +590,14 @@ std::pair, std::vector> hash_partition_table( input, gather_map.begin(), output_cols, detail::gather_bitmask_op::DONT_CHECK, stream, mr); } - auto output{std::make_unique(std::move(output_cols))}; - return std::make_pair(std::move(output), std::move(partition_offsets)); + stream.synchronize(); // Async D2H copy must finish before returning host vec + return std::make_pair(std::make_unique
(std::move(output_cols)), + std::move(partition_offsets)); } else { // Compute a scatter map from input to output such that the output rows are // sorted by partition number - auto row_output_locations{row_partition_numbers.data().get()}; - auto scanned_block_partition_sizes_ptr{scanned_block_partition_sizes.data().get()}; + auto row_output_locations{row_partition_numbers.data()}; + auto scanned_block_partition_sizes_ptr{scanned_block_partition_sizes.data()}; compute_row_output_locations<<, std::vector> hash_partition_table( auto output = detail::scatter( input, row_partition_numbers.begin(), row_partition_numbers.end(), input, false, stream, mr); + stream.synchronize(); // Async D2H copy must finish before returning host vec return std::make_pair(std::move(output), std::move(partition_offsets)); } } @@ -648,7 +643,7 @@ struct dispatch_map_type { rmm::mr::device_memory_resource* mr) const { // Build a histogram of the number of rows in each partition - rmm::device_vector histogram(num_partitions + 1); + rmm::device_uvector histogram(num_partitions + 1, stream); std::size_t temp_storage_bytes{}; std::size_t const num_levels = num_partitions + 1; size_type const lower_level = 0; @@ -656,7 +651,7 @@ struct dispatch_map_type { cub::DeviceHistogram::HistogramEven(nullptr, temp_storage_bytes, partition_map.begin(), - histogram.data().get(), + histogram.data(), num_levels, lower_level, upper_level, @@ -668,7 +663,7 @@ struct dispatch_map_type { cub::DeviceHistogram::HistogramEven(temp_storage.data(), temp_storage_bytes, partition_map.begin(), - histogram.data().get(), + histogram.data(), num_levels, lower_level, upper_level, @@ -680,13 +675,12 @@ struct dispatch_map_type { thrust::exclusive_scan( rmm::exec_policy(stream), histogram.begin(), histogram.end(), histogram.begin()); - // Copy offsets to host - std::vector partition_offsets(histogram.size()); - thrust::copy(histogram.begin(), histogram.end(), partition_offsets.begin()); + // Copy offsets to host before the transform below modifies the histogram + auto const partition_offsets = cudf::detail::make_std_vector_sync(histogram, stream); // Unfortunately need to materialize the scatter map because // `detail::scatter` requires multiple passes through the iterator - rmm::device_vector scatter_map(partition_map.size()); + rmm::device_uvector scatter_map(partition_map.size(), stream); // For each `partition_map[i]`, atomically increment the corresponding // partition offset to determine `i`s location in the output @@ -694,7 +688,7 @@ struct dispatch_map_type { partition_map.begin(), partition_map.end(), scatter_map.begin(), - [offsets = histogram.data().get()] __device__(auto partition_number) { + [offsets = histogram.data()] __device__(auto partition_number) { return atomicAdd(&offsets[partition_number], 1); }); diff --git a/cpp/src/partitioning/round_robin.cu b/cpp/src/partitioning/round_robin.cu index e516d0a51a5..80beb6e715c 100644 --- a/cpp/src/partitioning/round_robin.cu +++ b/cpp/src/partitioning/round_robin.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,7 @@ #include #include -#include +#include #include #include @@ -44,8 +45,7 @@ #include namespace { -template -using VectorT = rmm::device_vector; + /** * @brief Handles the "degenerate" case num_partitions >= num_rows. * @@ -84,7 +84,6 @@ std::pair, std::vector> degenerate auto nrows = input.num_rows(); // iterator for partition index rotated right by start_partition positions: - // auto rotated_iter_begin = thrust::make_transform_iterator( thrust::make_counting_iterator(0), [num_partitions, start_partition] __device__(auto index) { @@ -92,7 +91,7 @@ std::pair, std::vector> degenerate }); if (num_partitions == nrows) { - VectorT partition_offsets(num_partitions, cudf::size_type{0}); + rmm::device_uvector partition_offsets(num_partitions, stream); thrust::sequence(rmm::exec_policy(stream), partition_offsets.begin(), partition_offsets.end()); auto uniq_tbl = cudf::detail::gather(input, @@ -102,25 +101,14 @@ std::pair, std::vector> degenerate stream, mr); - auto ret_pair = - std::make_pair(std::move(uniq_tbl), std::vector(num_partitions)); - - CUDA_TRY(cudaMemcpyAsync(ret_pair.second.data(), - partition_offsets.data().get(), - sizeof(cudf::size_type) * num_partitions, - cudaMemcpyDeviceToHost, - stream.value())); - - stream.synchronize(); - - return ret_pair; + return std::make_pair(std::move(uniq_tbl), + cudf::detail::make_std_vector_sync(partition_offsets, stream)); } else { //( num_partitions > nrows ) - VectorT d_row_indices(nrows, cudf::size_type{0}); + rmm::device_uvector d_row_indices(nrows, stream); // copy rotated right partition indexes that // fall in the interval [0, nrows): //(this relies on a _stable_ copy_if()) - // thrust::copy_if(rmm::exec_policy(stream), rotated_iter_begin, rotated_iter_begin + num_partitions, @@ -128,7 +116,6 @@ std::pair, std::vector> degenerate [nrows] __device__(auto index) { return (index < nrows); }); //...and then use the result, d_row_indices, as gather map: - // auto uniq_tbl = cudf::detail::gather(input, d_row_indices.begin(), d_row_indices.end(), // map @@ -136,34 +123,22 @@ std::pair, std::vector> degenerate stream, mr); - auto ret_pair = - std::make_pair(std::move(uniq_tbl), std::vector(num_partitions)); - // offsets (part 1: compute partition sizes); // iterator for number of edges of the transposed bipartite graph; // this composes rotated_iter transform (above) iterator with // calculating number of edges of transposed bi-graph: - // auto nedges_iter_begin = thrust::make_transform_iterator( rotated_iter_begin, [nrows] __device__(auto index) { return (index < nrows ? 1 : 0); }); // offsets (part 2: compute partition offsets): - // - VectorT partition_offsets(num_partitions, cudf::size_type{0}); + rmm::device_uvector partition_offsets(num_partitions, stream); thrust::exclusive_scan(rmm::exec_policy(stream), nedges_iter_begin, nedges_iter_begin + num_partitions, partition_offsets.begin()); - CUDA_TRY(cudaMemcpyAsync(ret_pair.second.data(), - partition_offsets.data().get(), - sizeof(cudf::size_type) * num_partitions, - cudaMemcpyDeviceToHost, - stream.value())); - - stream.synchronize(); - - return ret_pair; + return std::make_pair(std::move(uniq_tbl), + cudf::detail::make_std_vector_sync(partition_offsets, stream)); } } } // namespace diff --git a/cpp/tests/partitioning/hash_partition_test.cpp b/cpp/tests/partitioning/hash_partition_test.cpp index 97c61c10718..1a39c7701f6 100644 --- a/cpp/tests/partitioning/hash_partition_test.cpp +++ b/cpp/tests/partitioning/hash_partition_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, NVIDIA CORPORATION. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ #include #include #include +#include "cudf/detail/utilities/vector_factories.hpp" using cudf::test::fixed_width_column_wrapper; using cudf::test::strings_column_wrapper; @@ -310,15 +311,15 @@ void run_fixed_width_test(size_t cols, // Compute the partition number for each row cudf::size_type partition = 0; - thrust::host_vector partitions; + std::vector partitions; std::for_each(offsets1.begin() + 1, offsets1.end(), [&](cudf::size_type const& count) { std::fill_n(std::back_inserter(partitions), count, partition++); }); // Make a table view of the partition numbers constexpr cudf::data_type dtype{cudf::type_id::INT32}; - rmm::device_vector d_partitions(partitions); - cudf::column_view partitions_col(dtype, rows, d_partitions.data().get()); + auto d_partitions = cudf::detail::make_device_uvector_sync(partitions); + cudf::column_view partitions_col(dtype, rows, d_partitions.data()); cudf::table_view partitions_table({partitions_col}); // Sort partition numbers by the corresponding row hashes of each output From 1757d1005c81daf9dbbf434b2d63c57127914a02 Mon Sep 17 00:00:00 2001 From: Rong Ou Date: Thu, 29 Apr 2021 10:59:59 -0700 Subject: [PATCH 05/65] ensure cuFile JNI library is loaded before any use (#8105) Followup to #8053 to make sure `libcufilejni.so` is always loaded before creating `CuFileBuffer` or `CuFileHandle`. Authors: - Rong Ou (https://github.com/rongou) Approvers: - Jason Lowe (https://github.com/jlowe) URL: https://github.com/rapidsai/cudf/pull/8105 --- java/src/main/java/ai/rapids/cudf/CuFile.java | 4 ++-- java/src/main/java/ai/rapids/cudf/CuFileBuffer.java | 5 ++++- java/src/main/java/ai/rapids/cudf/CuFileDriver.java | 4 ++-- java/src/main/java/ai/rapids/cudf/CuFileHandle.java | 4 ++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/java/src/main/java/ai/rapids/cudf/CuFile.java b/java/src/main/java/ai/rapids/cudf/CuFile.java index 1829ac093f8..97739ad4f65 100644 --- a/java/src/main/java/ai/rapids/cudf/CuFile.java +++ b/java/src/main/java/ai/rapids/cudf/CuFile.java @@ -45,7 +45,7 @@ public class CuFile { * Load the native libraries needed for libcufilejni, if not loaded already; open the cuFile * driver, and add a shutdown hook to close it. */ - private static synchronized void initialize() { + static synchronized void initialize() { if (!initialized) { try { NativeDepsLoader.loadNativeDeps(new String[]{"cufilejni"}); @@ -55,7 +55,7 @@ private static synchronized void initialize() { })); initialized = true; } catch (Throwable t) { - log.error("Could not load cuFile jni library...", t); + throw new RuntimeException(t); } } } diff --git a/java/src/main/java/ai/rapids/cudf/CuFileBuffer.java b/java/src/main/java/ai/rapids/cudf/CuFileBuffer.java index 574aa753986..082a6c6994e 100644 --- a/java/src/main/java/ai/rapids/cudf/CuFileBuffer.java +++ b/java/src/main/java/ai/rapids/cudf/CuFileBuffer.java @@ -21,10 +21,13 @@ */ public final class CuFileBuffer extends BaseDeviceMemoryBuffer { private static final int ALIGNMENT = 4096; - private final DeviceMemoryBuffer deviceMemoryBuffer; private final CuFileResourceCleaner cleaner; + static { + CuFile.initialize(); + } + /** * Construct a new cuFile buffer. * diff --git a/java/src/main/java/ai/rapids/cudf/CuFileDriver.java b/java/src/main/java/ai/rapids/cudf/CuFileDriver.java index 78afe617e43..1934abf0dbd 100644 --- a/java/src/main/java/ai/rapids/cudf/CuFileDriver.java +++ b/java/src/main/java/ai/rapids/cudf/CuFileDriver.java @@ -19,10 +19,10 @@ /** * Represents a cuFile driver. */ -public final class CuFileDriver implements AutoCloseable { +final class CuFileDriver implements AutoCloseable { private final CuFileResourceCleaner cleaner; - public CuFileDriver() { + CuFileDriver() { cleaner = new CuFileResourceCleaner(create(), CuFileDriver::destroy); MemoryCleaner.register(this, cleaner); } diff --git a/java/src/main/java/ai/rapids/cudf/CuFileHandle.java b/java/src/main/java/ai/rapids/cudf/CuFileHandle.java index 5e5e1035af7..2648e0a2d7c 100644 --- a/java/src/main/java/ai/rapids/cudf/CuFileHandle.java +++ b/java/src/main/java/ai/rapids/cudf/CuFileHandle.java @@ -22,6 +22,10 @@ abstract class CuFileHandle implements AutoCloseable { private final CuFileResourceCleaner cleaner; + static { + CuFile.initialize(); + } + protected CuFileHandle(long pointer) { cleaner = new CuFileResourceCleaner(pointer, CuFileHandle::destroy); MemoryCleaner.register(this, cleaner); From 04d6e5aef09d409351f8485121d5d54d6abbceca Mon Sep 17 00:00:00 2001 From: Liangcai Li Date: Fri, 30 Apr 2021 03:24:16 +0800 Subject: [PATCH 06/65] JNI support for scalar of list (#8077) This PR is to add the JNI support for scalar of list, along with building a ColumnVector from a list scalar. Since the PR https://github.com/rapidsai/cudf/pull/7584 inroduced the list scalar in cpp. Signed-off-by: Firestarman Authors: - Liangcai Li (https://github.com/firestarman) Approvers: - Jason Lowe (https://github.com/jlowe) URL: https://github.com/rapidsai/cudf/pull/8077 --- .../main/java/ai/rapids/cudf/ColumnView.java | 11 ++- java/src/main/java/ai/rapids/cudf/Scalar.java | 46 +++++++++++ java/src/main/native/src/ColumnVectorJni.cpp | 79 ++++++++++++++++++- java/src/main/native/src/CudaJni.cpp | 9 ++- java/src/main/native/src/ScalarJni.cpp | 34 +++++++- java/src/main/native/src/cudf_jni_apis.hpp | 7 ++ .../java/ai/rapids/cudf/ColumnVectorTest.java | 76 ++++++++++++++++-- .../test/java/ai/rapids/cudf/ScalarTest.java | 33 +++++++- 8 files changed, 283 insertions(+), 12 deletions(-) diff --git a/java/src/main/java/ai/rapids/cudf/ColumnView.java b/java/src/main/java/ai/rapids/cudf/ColumnView.java index 402c64dd83d..f6f4891cdcb 100644 --- a/java/src/main/java/ai/rapids/cudf/ColumnView.java +++ b/java/src/main/java/ai/rapids/cudf/ColumnView.java @@ -41,7 +41,7 @@ public class ColumnView implements AutoCloseable, BinaryOperable { * Constructs a Column View given a native view address * @param address the view handle */ - protected ColumnView(long address) { + ColumnView(long address) { this.viewHandle = address; this.type = DType.fromNative(ColumnView.getNativeTypeId(viewHandle), ColumnView.getNativeTypeScale(viewHandle)); this.rows = ColumnView.getNativeRowCount(viewHandle); @@ -211,6 +211,15 @@ public void close() { viewHandle = 0; } + @Override + public String toString() { + return "ColumnView{" + + "rows=" + rows + + ", type=" + type + + ", nullCount=" + nullCount + + '}'; + } + /** * Used for string strip function. * Indicates characters to be stripped from the beginning, end, or both of each string. diff --git a/java/src/main/java/ai/rapids/cudf/Scalar.java b/java/src/main/java/ai/rapids/cudf/Scalar.java index 4221b394826..94aef19128a 100644 --- a/java/src/main/java/ai/rapids/cudf/Scalar.java +++ b/java/src/main/java/ai/rapids/cudf/Scalar.java @@ -86,6 +86,8 @@ public static Scalar fromNull(DType type) { return new Scalar(type, makeDecimal32Scalar(0, type.getScale(), false)); case DECIMAL64: return new Scalar(type, makeDecimal64Scalar(0L, type.getScale(), false)); + case LIST: + return new Scalar(type, makeListScalar(0L, false)); default: throw new IllegalArgumentException("Unexpected type: " + type); } @@ -333,6 +335,19 @@ public static Scalar fromString(String value) { return new Scalar(DType.STRING, makeStringScalar(value.getBytes(StandardCharsets.UTF_8), true)); } + /** + * Creates a scalar of list from a ColumnView. + * + * All the rows in the ColumnView will be copied into the Scalar. So the ColumnView + * can be closed after this call completes. + */ + public static Scalar listFromColumnView(ColumnView list) { + if (list == null) { + return Scalar.fromNull(DType.LIST); + } + return new Scalar(DType.LIST, makeListScalar(list.getNativeView(), true)); + } + private static native void closeScalar(long scalarHandle); private static native boolean isScalarValid(long scalarHandle); private static native byte getByte(long scalarHandle); @@ -342,6 +357,7 @@ public static Scalar fromString(String value) { private static native float getFloat(long scalarHandle); private static native double getDouble(long scalarHandle); private static native byte[] getUTF8(long scalarHandle); + private static native long getListAsColumnView(long scalarHandle); private static native long makeBool8Scalar(boolean isValid, boolean value); private static native long makeInt8Scalar(byte value, boolean isValid); private static native long makeUint8Scalar(byte value, boolean isValid); @@ -360,6 +376,7 @@ public static Scalar fromString(String value) { private static native long makeTimestampTimeScalar(int dtypeNativeId, long value, boolean isValid); private static native long makeDecimal32Scalar(int value, int scale, boolean isValid); private static native long makeDecimal64Scalar(long value, int scale, boolean isValid); + private static native long makeListScalar(long viewHandle, boolean isValid); Scalar(DType type, long scalarHandle) { @@ -484,6 +501,19 @@ public byte[] getUTF8() { return getUTF8(getScalarHandle()); } + /** + * Returns the scalar value as a ColumnView. Callers should close the returned ColumnView to + * avoid memory leak. + * + * The returned ColumnView is only valid as long as the Scalar remains valid. If the Scalar + * is closed before this ColumnView is closed, using this ColumnView will result in undefined + * behavior. + */ + public ColumnView getListAsColumnView() { + assert DType.LIST.equals(type) : "Cannot get list for the vector of type " + type; + return new ColumnView(getListAsColumnView(getScalarHandle())); + } + @Override public ColumnVector binaryOp(BinaryOp op, BinaryOperable rhs, DType outType) { if (rhs instanceof ColumnView) { @@ -541,6 +571,11 @@ public boolean equals(Object o) { return getLong() == other.getLong(); case STRING: return Arrays.equals(getUTF8(), other.getUTF8()); + case LIST: + try (ColumnView viewMe = getListAsColumnView(); + ColumnView viewO = other.getListAsColumnView()) { + return viewMe.equals(viewO); + } default: throw new IllegalStateException("Unexpected type: " + type); } @@ -589,6 +624,11 @@ public int hashCode() { case STRING: valueHash = Arrays.hashCode(getUTF8()); break; + case LIST: + try (ColumnView v = getListAsColumnView()) { + valueHash = v.hashCode(); + } + break; default: throw new IllegalStateException("Unknown scalar type: " + type); } @@ -651,6 +691,12 @@ public String toString() { case DECIMAL64: sb.append(getBigDecimal()); break; + case LIST: + try (ColumnView v = getListAsColumnView()) { + // It's not easy to pull out the elements so just a simple string of some metadata. + sb.append(v.toString()); + } + break; default: throw new IllegalArgumentException("Unknown scalar type: " + type); } diff --git a/java/src/main/native/src/ColumnVectorJni.cpp b/java/src/main/native/src/ColumnVectorJni.cpp index ba0e4f05714..858dcf6fd5d 100644 --- a/java/src/main/native/src/ColumnVectorJni.cpp +++ b/java/src/main/native/src/ColumnVectorJni.cpp @@ -31,6 +31,55 @@ #include "cudf_jni_apis.hpp" #include "dtype_utils.hpp" +namespace cudf { +namespace jni { + /** + * @brief Creates an empty column according to the type tree specified + * in @p view + * + * An empty column contains zero elements and no validity mask. + * + * Unlike the 'cudf::make_empty_column', it takes care of the nested type by + * iterating the children columns in the @p view + * + * @param[in] view The input column view + * @return An empty column for the input column view + */ + std::unique_ptr make_empty_column(JNIEnv *env, cudf::column_view const& view) { + auto tid = view.type().id(); + if (tid == cudf::type_id::LIST) { + // List + if (view.num_children() != 2) { + throw jni_exception("List type requires two children(offset, data)."); + } + // Only needs the second child. + auto data_view = view.child(1); + // offsets: [0] + auto offsets_buffer = rmm::device_buffer(sizeof(cudf::size_type)); + device_memset_async(env, offsets_buffer, 0); + auto offsets = std::make_unique(cudf::data_type{cudf::type_id::INT32}, 1, + std::move(offsets_buffer), + rmm::device_buffer(), 0); + auto data_col = make_empty_column(env, data_view); + return cudf::make_lists_column(0, std::move(offsets), std::move(data_col), + 0, rmm::device_buffer()); + } else if (tid == cudf::type_id::STRUCT) { + // Struct + std::vector> children(view.num_children()); + std::transform(view.child_begin(), view.child_end(), children.begin(), + [env](auto const& child_v) { + return make_empty_column(env, child_v); + }); + return cudf::make_structs_column(0, std::move(children), 0, rmm::device_buffer()); + } else { + // Non nested types + return cudf::make_empty_column(view.type()); + } + } + +} // namespace jni +} // namespace cudf + extern "C" { JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_ColumnVector_sequence(JNIEnv *env, jclass, @@ -168,6 +217,7 @@ JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_ColumnVector_makeList(JNIEnv *env, j JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_ColumnVector_fromScalar(JNIEnv *env, jclass, jlong j_scalar, jint row_count) { + using ScalarType = cudf::scalar_type_t; JNI_NULL_CHECK(env, j_scalar, "scalar is null", 0); try { cudf::jni::auto_set_device(env); @@ -176,7 +226,34 @@ JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_ColumnVector_fromScalar(JNIEnv *env, cudf::mask_state mask_state = scalar_val->is_valid() ? cudf::mask_state::UNALLOCATED : cudf::mask_state::ALL_NULL; std::unique_ptr col; - if (row_count == 0) { + if (dtype.id() == cudf::type_id::LIST) { + // Neither 'cudf::make_empty_column' nor 'cudf::make_column_from_scalar' supports + // LIST type for now (https://github.com/rapidsai/cudf/issues/8088), so the list + // precedes the others and takes care of the empty column itself. + auto s_list = reinterpret_cast(scalar_val); + cudf::column_view s_val = s_list->view(); + + // Offsets: [0, list_size, list_size*2, ..., list_szie*row_count] + auto zero = cudf::make_numeric_scalar(cudf::data_type(cudf::type_id::INT32)); + auto step = cudf::make_numeric_scalar(cudf::data_type(cudf::type_id::INT32)); + zero->set_valid(true); + step->set_valid(true); + static_cast(zero.get())->set_value(0); + static_cast(step.get())->set_value(s_val.size()); + std::unique_ptr offsets = cudf::sequence(row_count + 1, *zero, *step); + // Data: + // Builds the data column by leveraging `cudf::concatenate` to repeat the 's_val' + // 'row_count' times, because 'cudf::make_column_from_scalar' does not support list + // type. + // (Assumes the `row_count` is not big, otherwise there would be a performance issue.) + // Checks the `row_count` because `cudf::concatenate` does not support no columns. + auto data_col = row_count > 0 + ? cudf::concatenate(std::vector(row_count, s_val)) + : cudf::jni::make_empty_column(env, s_val); + col = cudf::make_lists_column(row_count, std::move(offsets), std::move(data_col), + cudf::state_null_count(mask_state, row_count), + cudf::create_null_mask(row_count, mask_state)); + } else if (row_count == 0) { col = cudf::make_empty_column(dtype); } else if (cudf::is_fixed_width(dtype)) { col = cudf::make_fixed_width_column(dtype, row_count, mask_state); diff --git a/java/src/main/native/src/CudaJni.cpp b/java/src/main/native/src/CudaJni.cpp index b41fae21a74..f5eb09fa2d4 100644 --- a/java/src/main/native/src/CudaJni.cpp +++ b/java/src/main/native/src/CudaJni.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include "jni_utils.hpp" namespace { @@ -47,6 +48,12 @@ void auto_set_device(JNIEnv *env) { } } +/** Fills all the bytes in the buffer 'buf' with 'value'. */ +void device_memset_async(JNIEnv *env, rmm::device_buffer& buf, char value) { + cudaError_t cuda_status = cudaMemsetAsync((void *)buf.data(), value, buf.size()); + jni_cuda_check(env, cuda_status); +} + } // namespace jni } // namespace cudf diff --git a/java/src/main/native/src/ScalarJni.cpp b/java/src/main/native/src/ScalarJni.cpp index 4e74cab9328..275b8b051be 100644 --- a/java/src/main/native/src/ScalarJni.cpp +++ b/java/src/main/native/src/ScalarJni.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -125,6 +125,19 @@ JNIEXPORT jbyteArray JNICALL Java_ai_rapids_cudf_Scalar_getUTF8(JNIEnv *env, jcl CATCH_STD(env, 0); } +JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Scalar_getListAsColumnView(JNIEnv *env, jclass, + jlong scalar_handle) { + JNI_NULL_CHECK(env, scalar_handle, "scalar handle is null", 0); + try { + cudf::jni::auto_set_device(env); + auto s = reinterpret_cast(scalar_handle); + // Creates a column view in heap with the stack one, to let JVM take care of its + // life cycle. + return reinterpret_cast(new cudf::column_view(s->view())); + } + CATCH_STD(env, 0); +} + JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Scalar_makeBool8Scalar(JNIEnv *env, jclass, jboolean value, jboolean is_valid) { @@ -445,4 +458,23 @@ JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Scalar_binaryOpSV(JNIEnv *env, jclas CATCH_STD(env, 0); } +JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Scalar_makeListScalar(JNIEnv *env, jclass, + jlong view_handle, + jboolean is_valid) { + if (is_valid) { + JNI_NULL_CHECK(env, view_handle, + "list scalar is set to `valid` but column view is null", 0); + } + try { + cudf::jni::auto_set_device(env); + auto col_view = reinterpret_cast(view_handle); + + cudf::scalar* s = is_valid ? new cudf::list_scalar(*col_view) + : new cudf::list_scalar(); + s->set_valid(is_valid); + return reinterpret_cast(s); + } + CATCH_STD(env, 0); +} + } // extern "C" diff --git a/java/src/main/native/src/cudf_jni_apis.hpp b/java/src/main/native/src/cudf_jni_apis.hpp index 76c7e91d335..14999156890 100644 --- a/java/src/main/native/src/cudf_jni_apis.hpp +++ b/java/src/main/native/src/cudf_jni_apis.hpp @@ -70,5 +70,12 @@ void set_cudf_device(int device); */ void auto_set_device(JNIEnv *env); +/** + * Fills all the bytes in the buffer 'buf' with 'value'. + * The operation has not necessarily completed when this returns, but it could overlap with + * operations occurring on other streams. + */ +void device_memset_async(JNIEnv *env, rmm::device_buffer& buf, char value); + } // namespace jni } // namespace cudf diff --git a/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java b/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java index 36123704ae6..cca7090b8c7 100644 --- a/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java +++ b/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java @@ -1173,10 +1173,14 @@ void testFromScalarZeroRows() { case DURATION_NANOSECONDS: s = Scalar.durationFromLong(DType.create(type), 21313); break; - case EMPTY: - case LIST: - case STRUCT: - continue; + case EMPTY: + case STRUCT: + continue; + case LIST: + try (ColumnVector list = ColumnVector.fromInts(1, 2, 3)) { + s = Scalar.listFromColumnView(list); + } + break; default: throw new IllegalArgumentException("Unexpected type: " + type); } @@ -1349,9 +1353,20 @@ void testFromScalar() { break; } case EMPTY: - case LIST: case STRUCT: continue; + case LIST: + try (ColumnVector list = ColumnVector.fromInts(1, 2, 3)) { + s = Scalar.listFromColumnView(list); + expected = ColumnVector.fromLists( + new HostColumnVector.ListType(true, + new HostColumnVector.BasicType(true, DType.INT32)), + Arrays.asList(1, 2, 3), + Arrays.asList(1, 2, 3), + Arrays.asList(1, 2, 3), + Arrays.asList(1, 2, 3)); + } + break; default: throw new IllegalArgumentException("Unexpected type: " + type); } @@ -1376,7 +1391,7 @@ void testFromScalar() { void testFromScalarNull() { final int rowCount = 4; for (DType.DTypeEnum typeEnum : DType.DTypeEnum.values()) { - if (typeEnum == DType.DTypeEnum.EMPTY || typeEnum == DType.DTypeEnum.LIST || typeEnum == DType.DTypeEnum.STRUCT) { + if (typeEnum == DType.DTypeEnum.EMPTY || typeEnum == DType.DTypeEnum.STRUCT) { continue; } DType dType; @@ -1413,6 +1428,55 @@ void testFromScalarNullByte() { } } + @Test + void testFromScalarListOfList() { + HostColumnVector.DataType childType = new HostColumnVector.ListType(true, + new HostColumnVector.BasicType(true, DType.INT32)); + HostColumnVector.DataType resultType = new HostColumnVector.ListType(true, childType); + try (ColumnVector list = ColumnVector.fromLists(childType, + Arrays.asList(1, 2, 3), + Arrays.asList(4, 5, 6)); + Scalar s = Scalar.listFromColumnView(list)) { + try (ColumnVector ret = ColumnVector.fromScalar(s, 2); + ColumnVector expected = ColumnVector.fromLists(resultType, + Arrays.asList(Arrays.asList(1, 2, 3),Arrays.asList(4, 5, 6)), + Arrays.asList(Arrays.asList(1, 2, 3),Arrays.asList(4, 5, 6)))) { + assertColumnsAreEqual(expected, ret); + } + // empty row + try (ColumnVector ret = ColumnVector.fromScalar(s, 0)) { + assertEquals(ret.getRowCount(), 0); + assertEquals(ret.getNullCount(), 0); + } + } + } + + @Test + void testFromScalarListOfStruct() { + HostColumnVector.DataType childType = new HostColumnVector.StructType(true, + new HostColumnVector.BasicType(true, DType.INT32), + new HostColumnVector.BasicType(true, DType.STRING)); + HostColumnVector.DataType resultType = new HostColumnVector.ListType(true, childType); + try (ColumnVector list = ColumnVector.fromStructs(childType, + new HostColumnVector.StructData(1, "s1"), + new HostColumnVector.StructData(2, "s2")); + Scalar s = Scalar.listFromColumnView(list)) { + try (ColumnVector ret = ColumnVector.fromScalar(s, 2); + ColumnVector expected = ColumnVector.fromLists(resultType, + Arrays.asList(new HostColumnVector.StructData(1, "s1"), + new HostColumnVector.StructData(2, "s2")), + Arrays.asList(new HostColumnVector.StructData(1, "s1"), + new HostColumnVector.StructData(2, "s2")))) { + assertColumnsAreEqual(expected, ret); + } + // empty row + try (ColumnVector ret = ColumnVector.fromScalar(s, 0)) { + assertEquals(ret.getRowCount(), 0); + assertEquals(ret.getNullCount(), 0); + } + } + } + @Test void testReplaceNullsScalarEmptyColumn() { try (ColumnVector input = ColumnVector.fromBoxedBooleans(); diff --git a/java/src/test/java/ai/rapids/cudf/ScalarTest.java b/java/src/test/java/ai/rapids/cudf/ScalarTest.java index 627171e4b2f..b8141d1601a 100644 --- a/java/src/test/java/ai/rapids/cudf/ScalarTest.java +++ b/java/src/test/java/ai/rapids/cudf/ScalarTest.java @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2019, NVIDIA CORPORATION. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,9 @@ import org.junit.jupiter.api.Test; import java.math.BigDecimal; +import java.util.Arrays; +import static ai.rapids.cudf.TableTest.assertColumnsAreEqual; import static org.junit.jupiter.api.Assertions.*; public class ScalarTest extends CudfTestBase { @@ -54,7 +56,7 @@ public void testNull() { } else { type = DType.create(dataType); } - if (!type.isNestedType()) { + if (!type.isNestedType() || type.equals(DType.LIST)) { try (Scalar s = Scalar.fromNull(type)) { assertEquals(type, s.getType()); assertFalse(s.isValid(), "null validity for " + type); @@ -205,4 +207,31 @@ public void testString() { assertArrayEquals(new byte[]{'T', 'E', 'S', 'T'}, s.getUTF8()); } } + + @Test + public void testList() { + // list of int + try (ColumnVector listInt = ColumnVector.fromInts(1, 2, 3, 4); + Scalar s = Scalar.listFromColumnView(listInt)) { + assertEquals(DType.LIST, s.getType()); + assertTrue(s.isValid()); + try (ColumnView v = s.getListAsColumnView()) { + assertColumnsAreEqual(listInt, v); + } + } + + // list of list + HostColumnVector.DataType listDT = new HostColumnVector.ListType(true, + new HostColumnVector.BasicType(true, DType.INT32)); + try (ColumnVector listList = ColumnVector.fromLists(listDT, + Arrays.asList(1, 2, 3), + Arrays.asList(4, 5, 6)); + Scalar s = Scalar.listFromColumnView(listList)) { + assertEquals(DType.LIST, s.getType()); + assertTrue(s.isValid()); + try (ColumnView v = s.getListAsColumnView()) { + assertColumnsAreEqual(listList, v); + } + } + } } From cea6c20805747804484689e1f19e27af92663ad5 Mon Sep 17 00:00:00 2001 From: jakirkham Date: Thu, 29 Apr 2021 16:54:51 -0700 Subject: [PATCH 07/65] Use `cupy.ndarray` (without `core`) (#8114) `core` is internal to `cupy` and renamed to reflect that in CuPy 9. However that shouldn't be needed to access `ndarray` as it is `import`ed at the top-level. So just use `cupy.ndarray`. cc @kkraus14 @rjzamora Authors: - https://github.com/jakirkham Approvers: - Keith Kraus (https://github.com/kkraus14) - GALI PREM SAGAR (https://github.com/galipremsagar) URL: https://github.com/rapidsai/cudf/pull/8114 --- python/cudf/cudf/core/algorithms.py | 2 +- python/cudf/cudf/tests/test_dataframe.py | 2 +- python/cudf/cudf/tests/test_factorize.py | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/python/cudf/cudf/core/algorithms.py b/python/cudf/cudf/core/algorithms.py index 12bd17e1f6a..af908a664dc 100644 --- a/python/cudf/cudf/core/algorithms.py +++ b/python/cudf/cudf/core/algorithms.py @@ -53,7 +53,7 @@ def factorize(values, sort=False, na_sentinel=-1, size_hint=None): if size_hint: warn("size_hint is not applicable for cudf.factorize") - return_cupy_array = isinstance(values, cp.core.core.ndarray) + return_cupy_array = isinstance(values, cp.ndarray) values = Series(values) diff --git a/python/cudf/cudf/tests/test_dataframe.py b/python/cudf/cudf/tests/test_dataframe.py index 06b08d79093..4890ccc289e 100644 --- a/python/cudf/cudf/tests/test_dataframe.py +++ b/python/cudf/cudf/tests/test_dataframe.py @@ -6069,7 +6069,7 @@ def test_dataframe_init_1d_list(data, columns): def test_dataframe_init_from_arrays_cols(data, cols, index): gd_data = data - if isinstance(data, cupy.core.ndarray): + if isinstance(data, cupy.ndarray): # pandas can't handle cupy arrays in general pd_data = data.get() diff --git a/python/cudf/cudf/tests/test_factorize.py b/python/cudf/cudf/tests/test_factorize.py index 61d11fa5961..cd0f870e0d5 100644 --- a/python/cudf/cudf/tests/test_factorize.py +++ b/python/cudf/cudf/tests/test_factorize.py @@ -20,7 +20,7 @@ def test_factorize_series_obj(ncats, nelem): uvals, labels = df["cats"].factorize() np.testing.assert_array_equal(labels.to_array(), sorted(set(arr))) - assert isinstance(uvals, cp.core.core.ndarray) + assert isinstance(uvals, cp.ndarray) assert isinstance(labels, Index) encoder = dict((labels[idx], idx) for idx in range(len(labels))) @@ -39,7 +39,7 @@ def test_factorize_index_obj(ncats, nelem): uvals, labels = df.index.factorize() np.testing.assert_array_equal(labels.values.get(), sorted(set(arr))) - assert isinstance(uvals, cp.core.core.ndarray) + assert isinstance(uvals, cp.ndarray) assert isinstance(labels, Index) encoder = dict((labels[idx], idx) for idx in range(len(labels))) @@ -127,15 +127,15 @@ def test_factorize_result_classes(): labels, cats = cudf.factorize(cudf.Series(data)) - assert isinstance(labels, cp.core.core.ndarray) + assert isinstance(labels, cp.ndarray) assert isinstance(cats, cudf.Index) labels, cats = cudf.factorize(cudf.Index(data)) - assert isinstance(labels, cp.core.core.ndarray) + assert isinstance(labels, cp.ndarray) assert isinstance(cats, cudf.Index) labels, cats = cudf.factorize(cp.array(data)) - assert isinstance(labels, cp.core.core.ndarray) - assert isinstance(cats, cp.core.core.ndarray) + assert isinstance(labels, cp.ndarray) + assert isinstance(cats, cp.ndarray) From e6f3f37ae6766cfeef8d8d08474708612cae770d Mon Sep 17 00:00:00 2001 From: MithunR Date: Fri, 30 Apr 2021 07:46:41 -0700 Subject: [PATCH 08/65] Switch from std::tie() to structured binding. (#8117) Addendum to #7866, to switch from using `std::tie()` to structured bindings, as prescribed for C++17. This required workarounds for compiler restrictions on using aliases from structured bindings as captures in lambdas. Authors: - MithunR (https://github.com/mythrocks) Approvers: - Conor Hoekstra (https://github.com/codereport) - Vukasin Milovanovic (https://github.com/vuule) - Christopher Harris (https://github.com/cwharris) - Nghia Truong (https://github.com/ttnghia) URL: https://github.com/rapidsai/cudf/pull/8117 --- cpp/src/rolling/grouped_rolling.cu | 40 ++++++++++++++---------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/cpp/src/rolling/grouped_rolling.cu b/cpp/src/rolling/grouped_rolling.cu index e2f2e8b07dc..1417c22ca31 100644 --- a/cpp/src/rolling/grouped_rolling.cu +++ b/cpp/src/rolling/grouped_rolling.cu @@ -313,13 +313,12 @@ std::unique_ptr range_window_ASC(column_view const& input, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { - size_type nulls_begin_idx, nulls_end_idx; - std::tie(nulls_begin_idx, nulls_end_idx) = get_null_bounds_for_orderby_column(orderby_column); + auto [h_nulls_begin_idx, h_nulls_end_idx] = get_null_bounds_for_orderby_column(orderby_column); auto preceding_calculator = - [nulls_begin_idx, - nulls_end_idx, - d_orderby = orderby_column.data(), + [nulls_begin_idx = h_nulls_begin_idx, + nulls_end_idx = h_nulls_end_idx, + d_orderby = orderby_column.data(), preceding_window, preceding_window_is_unbounded] __device__(size_type idx) -> size_type { if (preceding_window_is_unbounded) { @@ -351,10 +350,10 @@ std::unique_ptr range_window_ASC(column_view const& input, auto preceding_column = expand_to_column(preceding_calculator, input.size(), stream, mr); auto following_calculator = - [nulls_begin_idx, - nulls_end_idx, - num_rows = input.size(), - d_orderby = orderby_column.data(), + [nulls_begin_idx = h_nulls_begin_idx, + nulls_end_idx = h_nulls_end_idx, + num_rows = input.size(), + d_orderby = orderby_column.data(), following_window, following_window_is_unbounded] __device__(size_type idx) -> size_type { if (following_window_is_unbounded) { return num_rows - idx - 1; } @@ -471,8 +470,7 @@ std::unique_ptr range_window_ASC(column_view const& input, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { - rmm::device_vector null_start, null_end; - std::tie(null_start, null_end) = + auto [null_start, null_end] = get_null_bounds_for_orderby_column(orderby_column, group_offsets, stream); auto preceding_calculator = @@ -577,13 +575,12 @@ std::unique_ptr range_window_DESC(column_view const& input, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { - size_type nulls_begin_idx, nulls_end_idx; - std::tie(nulls_begin_idx, nulls_end_idx) = get_null_bounds_for_orderby_column(orderby_column); + auto [h_nulls_begin_idx, h_nulls_end_idx] = get_null_bounds_for_orderby_column(orderby_column); auto preceding_calculator = - [nulls_begin_idx, - nulls_end_idx, - d_orderby = orderby_column.data(), + [nulls_begin_idx = h_nulls_begin_idx, + nulls_end_idx = h_nulls_end_idx, + d_orderby = orderby_column.data(), preceding_window, preceding_window_is_unbounded] __device__(size_type idx) -> size_type { if (preceding_window_is_unbounded) { @@ -617,10 +614,10 @@ std::unique_ptr range_window_DESC(column_view const& input, auto preceding_column = expand_to_column(preceding_calculator, input.size(), stream, mr); auto following_calculator = - [nulls_begin_idx, - nulls_end_idx, - num_rows = input.size(), - d_orderby = orderby_column.data(), + [nulls_begin_idx = h_nulls_begin_idx, + nulls_end_idx = h_nulls_end_idx, + num_rows = input.size(), + d_orderby = orderby_column.data(), following_window, following_window_is_unbounded] __device__(size_type idx) -> size_type { if (following_window_is_unbounded) { return (num_rows - idx) - 1; } @@ -669,8 +666,7 @@ std::unique_ptr range_window_DESC(column_view const& input, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { - rmm::device_vector null_start, null_end; - std::tie(null_start, null_end) = + auto [null_start, null_end] = get_null_bounds_for_orderby_column(orderby_column, group_offsets, stream); auto preceding_calculator = From 322eac607513719fe9821cf82254adca87378db2 Mon Sep 17 00:00:00 2001 From: David Wendt <45795991+davidwendt@users.noreply.github.com> Date: Fri, 30 Apr 2021 11:47:43 -0400 Subject: [PATCH 09/65] Fix subword tokenizer to handle zero hash bin size (#8093) Closes #8051 The hashed vocabulary table contains hash-bin coefficients and offsets. Some bins may be created empty by the hash utility. The code was not handling this case properly and resolving a hash-table index to an invalid memory location. This PR adds a check for the hash bin size to prevent the invalid calculation. The PR also includes a gtest with an empty hash bin entry. Authors: - David Wendt (https://github.com/davidwendt) Approvers: - Mark Harris (https://github.com/harrism) - Nghia Truong (https://github.com/ttnghia) URL: https://github.com/rapidsai/cudf/pull/8093 --- cpp/src/text/subword/detail/hash_utils.cuh | 4 ++- cpp/tests/text/subword_tests.cpp | 35 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/cpp/src/text/subword/detail/hash_utils.cuh b/cpp/src/text/subword/detail/hash_utils.cuh index bc6b5c3dc1f..dc0737118e8 100644 --- a/cpp/src/text/subword/detail/hash_utils.cuh +++ b/cpp/src/text/subword/detail/hash_utils.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -157,6 +157,8 @@ __device__ int retrieve(uint64_t const key, auto const inner_bin_b = (bin_params >> 9) & ((1 << 7) - 1); auto const bin_size = static_cast(bin_params); + if (bin_size == 0) { return -1; } // key hash has no bin parameters + auto const inner_offset = hash(key, inner_bin_a, inner_bin_b, bin_size); auto const kv_pair = hash_table[start_ht_offset + inner_offset]; diff --git a/cpp/tests/text/subword_tests.cpp b/cpp/tests/text/subword_tests.cpp index 7324cf3ec6a..d23ca3033cc 100644 --- a/cpp/tests/text/subword_tests.cpp +++ b/cpp/tests/text/subword_tests.cpp @@ -339,3 +339,38 @@ TEST(TextSubwordTest, TokenizeWithSpecialTokens) CUDF_TEST_EXPECT_COLUMNS_EQUAL(result.tensor_attention_mask->view(), expected_attn); CUDF_TEST_EXPECT_COLUMNS_EQUAL(result.tensor_metadata->view(), expected_metadata); } + +TEST(TextSubwordTest, ZeroHashBinCoefficient) +{ + std::string hash_file = temp_env->get_temp_filepath("hashed_vocab.txt"); + { + std::ofstream outfile(hash_file, std::ofstream::out); + outfile << "26899\n27424\n2\n"; + outfile << "6321733446031528966 0\n0 0\n9\n"; // zeroes are here + outfile << "6206321707968233475\n3014663\n6205475701751152646\n"; + outfile << "451412623364\n5214737420442796033\n6173800107753209856\n"; + outfile << "0\n6356997\n6064762127302393858\n"; + outfile << "0\n1\n2\n"; + } + + std::vector h_strings{".zzzz"}; + cudf::test::strings_column_wrapper strings(h_strings.begin(), h_strings.end()); + auto vocab = nvtext::load_vocabulary_file(hash_file); + auto result = nvtext::subword_tokenize(cudf::strings_column_view{strings}, + *vocab, + 8, + 8, + true, // do_lower_case + true, // do_truncate + MAX_ROWS_TENSOR); + + // clang-format off + cudf::test::fixed_width_column_wrapper expected_tokens({7, 0, 0, 0, 0, 0, 0, 0}); + cudf::test::fixed_width_column_wrapper expected_attn( {1, 1, 0, 0, 0, 0, 0, 0}); + cudf::test::fixed_width_column_wrapper expected_metadata({0, 0, 1}); + // clang-format on + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(result.tensor_token_ids->view(), expected_tokens); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(result.tensor_attention_mask->view(), expected_attn); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(result.tensor_metadata->view(), expected_metadata); +} From f686c016631a6516c441228f28127eaa1ba097f8 Mon Sep 17 00:00:00 2001 From: Jordan Jacobelli Date: Fri, 30 Apr 2021 18:05:31 +0200 Subject: [PATCH 10/65] ENH Remove conda defaults channel in dev environments (#8122) Remove conda defaults channel in dev environments Authors: - Jordan Jacobelli (https://github.com/Ethyling) Approvers: - AJ Schmidt (https://github.com/ajschmidt8) URL: https://github.com/rapidsai/cudf/pull/8122 --- conda/environments/cudf_dev_cuda11.0.yml | 1 - conda/environments/cudf_dev_cuda11.1.yml | 1 - conda/environments/cudf_dev_cuda11.2.yml | 1 - 3 files changed, 3 deletions(-) diff --git a/conda/environments/cudf_dev_cuda11.0.yml b/conda/environments/cudf_dev_cuda11.0.yml index b789ff8587b..bb2b92cbbaf 100644 --- a/conda/environments/cudf_dev_cuda11.0.yml +++ b/conda/environments/cudf_dev_cuda11.0.yml @@ -6,7 +6,6 @@ channels: - nvidia - rapidsai-nightly - conda-forge - - defaults dependencies: - clang=8.0.1 - clang-tools=8.0.1 diff --git a/conda/environments/cudf_dev_cuda11.1.yml b/conda/environments/cudf_dev_cuda11.1.yml index 560a3d9f1e5..32e95123159 100644 --- a/conda/environments/cudf_dev_cuda11.1.yml +++ b/conda/environments/cudf_dev_cuda11.1.yml @@ -6,7 +6,6 @@ channels: - nvidia - rapidsai-nightly - conda-forge - - defaults dependencies: - clang=8.0.1 - clang-tools=8.0.1 diff --git a/conda/environments/cudf_dev_cuda11.2.yml b/conda/environments/cudf_dev_cuda11.2.yml index 24f7f7a1144..927a1ea12e0 100644 --- a/conda/environments/cudf_dev_cuda11.2.yml +++ b/conda/environments/cudf_dev_cuda11.2.yml @@ -6,7 +6,6 @@ channels: - nvidia - rapidsai-nightly - conda-forge - - defaults dependencies: - clang=8.0.1 - clang-tools=8.0.1 From aa61a6d9f870fadbcb50dea20d17859a325eb8d0 Mon Sep 17 00:00:00 2001 From: Rong Ou Date: Fri, 30 Apr 2021 11:07:44 -0700 Subject: [PATCH 11/65] don't throw an exception when cuFile jni can't be loaded (#8124) The CI/CD machine may not have GDS installed and need to skip cuFile JNI tests, so can't throw an exception. Authors: - Rong Ou (https://github.com/rongou) Approvers: - Jason Lowe (https://github.com/jlowe) URL: https://github.com/rapidsai/cudf/pull/8124 --- java/src/main/java/ai/rapids/cudf/CuFile.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/src/main/java/ai/rapids/cudf/CuFile.java b/java/src/main/java/ai/rapids/cudf/CuFile.java index 97739ad4f65..00c9cdb9fd5 100644 --- a/java/src/main/java/ai/rapids/cudf/CuFile.java +++ b/java/src/main/java/ai/rapids/cudf/CuFile.java @@ -55,7 +55,8 @@ static synchronized void initialize() { })); initialized = true; } catch (Throwable t) { - throw new RuntimeException(t); + // Cannot throw an exception here as the CI/CD machine may not have GDS installed. + log.error("Could not load cuFile jni library...", t); } } } From b368ebd7f42bca9993b6964e6a9488782e31614f Mon Sep 17 00:00:00 2001 From: Mark Harris Date: Sat, 1 May 2021 08:44:32 +1000 Subject: [PATCH 12/65] Convert unordered_multiset to use device_uvector (#8091) Converts `unordered_multiset` to use device_uvector instead of device_vector. Also adds a cudf::contains benchmark to SEARCH_BENCHMARK. Contributes to #7287 Performance of `cudf::contains`, the only user of this class, is significantly improved: ``` (rapids) rapids@compose:~/cudf/cpp/build/release$ _deps/benchmark-src/tools/compare.py benchmarks ~/cudf/cpp/build/contains_before.json ~/cudf/cpp/build/contains_after.json Comparing /home/mharris/rapids/cudf/cpp/build/contains_before.json to /home/mharris/rapids/cudf/cpp/build/contains_after.json Benchmark Time CPU Time Old Time New CPU Old CPU New -------------------------------------------------------------------------------------------------------------------------------------------------- Search/ColumnContains_AllValid/1024/manual_time -0.2608 -0.2074 0 0 0 0 Search/ColumnContains_AllValid/4096/manual_time -0.9039 -0.8854 1 0 1 0 Search/ColumnContains_AllValid/32768/manual_time -0.3135 -0.2648 0 0 0 0 Search/ColumnContains_AllValid/262144/manual_time -0.7520 -0.7421 0 0 0 0 Search/ColumnContains_AllValid/2097152/manual_time -0.2323 -0.2516 4 3 4 3 Search/ColumnContains_AllValid/16777216/manual_time -0.1821 -0.1856 40 32 40 32 Search/ColumnContains_AllValid/67108864/manual_time -0.1368 -0.1377 80 69 81 69 Search/ColumnContains_Nulls/1024/manual_time -0.2451 -0.1925 0 0 0 0 Search/ColumnContains_Nulls/4096/manual_time -0.2166 -0.1702 0 0 0 0 Search/ColumnContains_Nulls/32768/manual_time -0.1798 -0.1450 0 0 0 0 Search/ColumnContains_Nulls/262144/manual_time -0.1208 -0.1009 0 0 0 0 Search/ColumnContains_Nulls/2097152/manual_time -0.2312 -0.2696 3 2 3 2 Search/ColumnContains_Nulls/16777216/manual_time -0.2898 -0.2896 25 17 25 17 Search/ColumnContains_Nulls/67108864/manual_time -0.0884 -0.0891 68 62 68 62 ``` Authors: - Mark Harris (https://github.com/harrism) Approvers: - Mike Wilson (https://github.com/hyperbolic2346) - Nghia Truong (https://github.com/ttnghia) - Devavret Makkar (https://github.com/devavret) URL: https://github.com/rapidsai/cudf/pull/8091 --- cpp/benchmarks/CMakeLists.txt | 2 +- ...arch_benchmark.cu => search_benchmark.cpp} | 115 ++++++++++++------ cpp/src/hash/unordered_multiset.cuh | 29 +++-- 3 files changed, 93 insertions(+), 53 deletions(-) rename cpp/benchmarks/search/{search_benchmark.cu => search_benchmark.cpp} (63%) diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index 2125790a29b..3aeeaf71edd 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -97,7 +97,7 @@ ConfigureBench(ITERATOR_BENCH iterator/iterator_benchmark.cu) ################################################################################################### # - search benchmark ------------------------------------------------------------------------------ -ConfigureBench(SEARCH_BENCH search/search_benchmark.cu) +ConfigureBench(SEARCH_BENCH search/search_benchmark.cpp) ################################################################################################### # - sort benchmark -------------------------------------------------------------------------------- diff --git a/cpp/benchmarks/search/search_benchmark.cu b/cpp/benchmarks/search/search_benchmark.cpp similarity index 63% rename from cpp/benchmarks/search/search_benchmark.cu rename to cpp/benchmarks/search/search_benchmark.cpp index 7b4b8060514..7fb196fb500 100644 --- a/cpp/benchmarks/search/search_benchmark.cu +++ b/cpp/benchmarks/search/search_benchmark.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, NVIDIA CORPORATION. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,35 +31,6 @@ class Search : public cudf::benchmark { }; -void BM_non_null_column(benchmark::State& state) -{ - const cudf::size_type column_size{(cudf::size_type)state.range(0)}; - const cudf::size_type values_size = column_size; - - auto col_data_it = cudf::detail::make_counting_transform_iterator( - 0, [=](cudf::size_type row) { return static_cast(row); }); - auto val_data_it = cudf::detail::make_counting_transform_iterator( - 0, [=](cudf::size_type row) { return static_cast(values_size - row); }); - - cudf::test::fixed_width_column_wrapper column(col_data_it, col_data_it + column_size); - cudf::test::fixed_width_column_wrapper values(val_data_it, val_data_it + values_size); - - for (auto _ : state) { - cuda_event_timer timer(state, true); - auto col = cudf::upper_bound(cudf::table_view({column}), - cudf::table_view({values}), - {cudf::order::ASCENDING}, - {cudf::null_order::BEFORE}); - } -} - -BENCHMARK_DEFINE_F(Search, AllValidColumn)(::benchmark::State& state) { BM_non_null_column(state); } - -BENCHMARK_REGISTER_F(Search, AllValidColumn) - ->UseManualTime() - ->Unit(benchmark::kMillisecond) - ->Arg(100000000); - auto make_validity_iter() { static constexpr int r_min = 1; @@ -71,7 +42,7 @@ auto make_validity_iter() 0, [mod_base](auto row) { return (row % mod_base) > 0; }); } -void BM_nullable_column(benchmark::State& state) +void BM_column(benchmark::State& state, bool nulls) { const cudf::size_type column_size{(cudf::size_type)state.range(0)}; const cudf::size_type values_size = column_size; @@ -81,25 +52,39 @@ void BM_nullable_column(benchmark::State& state) auto val_data_it = cudf::detail::make_counting_transform_iterator( 0, [=](cudf::size_type row) { return static_cast(values_size - row); }); - cudf::test::fixed_width_column_wrapper column( - col_data_it, col_data_it + column_size, make_validity_iter()); - cudf::test::fixed_width_column_wrapper values( - val_data_it, val_data_it + values_size, make_validity_iter()); - - auto sorted = cudf::sort(cudf::table_view({column})); + auto column = [&]() { + return nulls ? cudf::test::fixed_width_column_wrapper( + col_data_it, col_data_it + column_size, make_validity_iter()) + : cudf::test::fixed_width_column_wrapper(col_data_it, + col_data_it + column_size); + }(); + auto values = [&]() { + return nulls ? cudf::test::fixed_width_column_wrapper( + val_data_it, val_data_it + values_size, make_validity_iter()) + : cudf::test::fixed_width_column_wrapper(val_data_it, + val_data_it + values_size); + }(); + + auto data_table = cudf::sort(cudf::table_view({column})); for (auto _ : state) { cuda_event_timer timer(state, true); - auto col = cudf::upper_bound(sorted->view(), + auto col = cudf::upper_bound(data_table->view(), cudf::table_view({values}), {cudf::order::ASCENDING}, {cudf::null_order::BEFORE}); } } -BENCHMARK_DEFINE_F(Search, NullableColumn)(::benchmark::State& state) { BM_nullable_column(state); } +BENCHMARK_DEFINE_F(Search, Column_AllValid)(::benchmark::State& state) { BM_column(state, false); } +BENCHMARK_DEFINE_F(Search, Column_Nulls)(::benchmark::State& state) { BM_column(state, true); } + +BENCHMARK_REGISTER_F(Search, Column_AllValid) + ->UseManualTime() + ->Unit(benchmark::kMillisecond) + ->Arg(100000000); -BENCHMARK_REGISTER_F(Search, NullableColumn) +BENCHMARK_REGISTER_F(Search, Column_Nulls) ->UseManualTime() ->Unit(benchmark::kMillisecond) ->Arg(100000000); @@ -153,3 +138,53 @@ BENCHMARK_REGISTER_F(Search, Table) ->UseManualTime() ->Unit(benchmark::kMillisecond) ->Apply(CustomArguments); + +void BM_contains(benchmark::State& state, bool nulls) +{ + const cudf::size_type column_size{(cudf::size_type)state.range(0)}; + const cudf::size_type values_size = column_size; + + auto col_data_it = cudf::detail::make_counting_transform_iterator( + 0, [=](cudf::size_type row) { return static_cast(row); }); + auto val_data_it = cudf::detail::make_counting_transform_iterator( + 0, [=](cudf::size_type row) { return static_cast(values_size - row); }); + + auto column = [&]() { + return nulls ? cudf::test::fixed_width_column_wrapper( + col_data_it, col_data_it + column_size, make_validity_iter()) + : cudf::test::fixed_width_column_wrapper(col_data_it, + col_data_it + column_size); + }(); + auto values = [&]() { + return nulls ? cudf::test::fixed_width_column_wrapper( + val_data_it, val_data_it + values_size, make_validity_iter()) + : cudf::test::fixed_width_column_wrapper(val_data_it, + val_data_it + values_size); + }(); + + for (auto _ : state) { + cuda_event_timer timer(state, true); + auto col = cudf::contains(column, values); + } +} + +BENCHMARK_DEFINE_F(Search, ColumnContains_AllValid)(::benchmark::State& state) +{ + BM_contains(state, false); +} +BENCHMARK_DEFINE_F(Search, ColumnContains_Nulls)(::benchmark::State& state) +{ + BM_contains(state, true); +} + +BENCHMARK_REGISTER_F(Search, ColumnContains_AllValid) + ->RangeMultiplier(8) + ->Ranges({{1 << 10, 1 << 26}}) + ->UseManualTime() + ->Unit(benchmark::kMillisecond); + +BENCHMARK_REGISTER_F(Search, ColumnContains_Nulls) + ->RangeMultiplier(8) + ->Ranges({{1 << 10, 1 << 26}}) + ->UseManualTime() + ->Unit(benchmark::kMillisecond); diff --git a/cpp/src/hash/unordered_multiset.cuh b/cpp/src/hash/unordered_multiset.cuh index 0704425186e..645d9bc5185 100644 --- a/cpp/src/hash/unordered_multiset.cuh +++ b/cpp/src/hash/unordered_multiset.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,11 @@ #include #include +#include +#include #include +#include #include namespace cudf { @@ -76,14 +79,16 @@ class unordered_multiset { auto d_column = column_device_view::create(col, stream); auto d_col = *d_column; - rmm::device_vector hash_bins_start(2 * d_col.size() + 1, size_type{0}); - rmm::device_vector hash_bins_end(2 * d_col.size() + 1, size_type{0}); - rmm::device_vector hash_data(d_col.size()); + auto hash_bins_start = + cudf::detail::make_zeroed_device_uvector_async(2 * d_col.size() + 1, stream); + auto hash_bins_end = + cudf::detail::make_zeroed_device_uvector_async(2 * d_col.size() + 1, stream); + auto hash_data = rmm::device_uvector(d_col.size(), stream); Hasher hasher; - size_type *d_hash_bins_start = hash_bins_start.data().get(); - size_type *d_hash_bins_end = hash_bins_end.data().get(); - Element *d_hash_data = hash_data.data().get(); + size_type *d_hash_bins_start = hash_bins_start.data(); + size_type *d_hash_bins_end = hash_bins_end.data(); + Element *d_hash_data = hash_data.data(); thrust::for_each(rmm::exec_policy(stream), thrust::make_counting_iterator(0), @@ -124,20 +129,20 @@ class unordered_multiset { unordered_multiset_device_view to_device() { return unordered_multiset_device_view( - size, hash_bins.data().get(), hash_data.data().get()); + size, hash_bins.data(), hash_data.data()); } private: unordered_multiset(size_type size, - rmm::device_vector &&hash_bins, - rmm::device_vector &&hash_data) + rmm::device_uvector &&hash_bins, + rmm::device_uvector &&hash_data) : size{size}, hash_bins{std::move(hash_bins)}, hash_data{std::move(hash_data)} { } size_type size; - rmm::device_vector hash_bins; - rmm::device_vector hash_data; + rmm::device_uvector hash_bins; + rmm::device_uvector hash_data; }; } // namespace detail From 7bf6de6c8c7dd819f4affaba4e8b993fac4ed465 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 30 Apr 2021 16:44:46 -0600 Subject: [PATCH 13/65] Fix `cudf_test/iterator_utilities.hpp` (#8126) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: * Replace `static` by `inline` qualifier for the functions to fix compilation error `error: ‘auto cudf::test::XXX(...)’ defined but not used`. * Add `iterator_all_nulls` function that returns an iterator which always yields `false`. * Add `iterator_no_null` function that returns an iterator which always yields `true`. The two new functions are required in some PRs for testing. Authors: - Nghia Truong (https://github.com/ttnghia) Approvers: - Vukasin Milovanovic (https://github.com/vuule) - MithunR (https://github.com/mythrocks) URL: https://github.com/rapidsai/cudf/pull/8126 --- cpp/include/cudf_test/iterator_utilities.hpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/cpp/include/cudf_test/iterator_utilities.hpp b/cpp/include/cudf_test/iterator_utilities.hpp index 297bcbf175c..f777ceed675 100644 --- a/cpp/include/cudf_test/iterator_utilities.hpp +++ b/cpp/include/cudf_test/iterator_utilities.hpp @@ -49,7 +49,7 @@ namespace test { * @return auto Validity iterator */ template -static auto iterator_with_null_at(Iter index_start, Iter index_end) +[[maybe_unused]] static auto iterator_with_null_at(Iter index_start, Iter index_end) { using index_type = typename std::iterator_traits::value_type; @@ -77,7 +77,7 @@ static auto iterator_with_null_at(Iter index_start, Iter index_end) * @param indices The indices for which the validity iterator must return `false` (i.e. null) * @return auto Validity iterator */ -static auto iterator_with_null_at(cudf::host_span const& indices) +[[maybe_unused]] static auto iterator_with_null_at(cudf::host_span indices) { return iterator_with_null_at(indices.begin(), indices.end()); } @@ -97,10 +97,24 @@ static auto iterator_with_null_at(cudf::host_span const& * @param index The index for which the validity iterator must return `false` (i.e. null) * @return auto Validity iterator */ -static auto iterator_with_null_at(cudf::size_type const& index) +[[maybe_unused]] static auto iterator_with_null_at(cudf::size_type index) { return iterator_with_null_at(std::vector{index}); } +/** + * @brief Bool iterator for marking all elements are null + * + * @return auto Validity iterator which always yields `false` + */ +[[maybe_unused]] static auto iterator_all_nulls() { return thrust::make_constant_iterator(false); } + +/** + * @brief Bool iterator for marking all elements are valid (non-null) + * + * @return auto Validity iterator which always yields `true` + */ +[[maybe_unused]] static auto iterator_no_null() { return thrust::make_constant_iterator(true); } + } // namespace test } // namespace cudf From 4869c237e48a3d72c5cf7815c24fc28cc6f46510 Mon Sep 17 00:00:00 2001 From: Karthikeyan <6488848+karthikeyann@users.noreply.github.com> Date: Sat, 1 May 2021 22:23:11 +0530 Subject: [PATCH 14/65] enable all aggregations for dictionary type (#8061) This PR enables all underlying type operations for dictionary type by calling the underlying type `update_target_element` except dictionary type to avoid recursion (and so only one depth of dictionary type is supported) Few 10.2 restrictions are removed. related disabled unit tests are enabled. unit test reformatted, clang-format Authors: - Karthikeyan (https://github.com/karthikeyann) Approvers: - Nghia Truong (https://github.com/ttnghia) - Christopher Harris (https://github.com/cwharris) - David Wendt (https://github.com/davidwendt) URL: https://github.com/rapidsai/cudf/pull/8061 --- .../cudf/detail/aggregation/aggregation.cuh | 131 +++------ cpp/tests/groupby/group_argmax_test.cpp | 152 +++++----- cpp/tests/groupby/group_argmin_test.cpp | 157 +++++----- cpp/tests/groupby/group_count_test.cpp | 176 ++++++------ cpp/tests/groupby/group_max_test.cpp | 178 ++++++------ cpp/tests/groupby/group_mean_test.cpp | 124 ++++---- cpp/tests/groupby/group_median_test.cpp | 118 ++++---- cpp/tests/groupby/group_min_test.cpp | 172 ++++++----- cpp/tests/groupby/group_nunique_test.cpp | 268 +++++++++--------- cpp/tests/groupby/group_product_test.cpp | 25 +- cpp/tests/groupby/group_quantile_test.cpp | 232 +++++++-------- cpp/tests/groupby/group_std_test.cpp | 155 +++++----- .../groupby/group_sum_of_squares_test.cpp | 125 ++++---- cpp/tests/groupby/group_sum_test.cpp | 145 +++++----- cpp/tests/groupby/group_var_test.cpp | 159 +++++------ 15 files changed, 1091 insertions(+), 1226 deletions(-) diff --git a/cpp/include/cudf/detail/aggregation/aggregation.cuh b/cpp/include/cudf/detail/aggregation/aggregation.cuh index 0d0b678f7af..d4833bcf91e 100644 --- a/cpp/include/cudf/detail/aggregation/aggregation.cuh +++ b/cpp/include/cudf/detail/aggregation/aggregation.cuh @@ -140,8 +140,6 @@ struct update_target_element; @@ -152,8 +150,6 @@ struct update_target_element(source.element(source_index))); if (target_has_nulls and target.is_null(target_index)) { target.set_valid(target_index); } - -#endif } }; @@ -190,8 +186,6 @@ struct update_target_element; @@ -202,8 +196,6 @@ struct update_target_element(source.element(source_index))); if (target_has_nulls and target.is_null(target_index)) { target.set_valid(target_index); } - -#endif } }; @@ -240,8 +232,6 @@ struct update_target_element; @@ -252,7 +242,6 @@ struct update_target_element(source.element(source_index))); if (target_has_nulls and target.is_null(target_index)) { target.set_valid(target_index); } -#endif } }; @@ -260,38 +249,55 @@ struct update_target_element() && !is_fixed_point()>* = nullptr> - __device__ void operator()(mutable_column_device_view& target, +template +struct update_target_from_dictionary { + template ()>* = nullptr> + __device__ void operator()(mutable_column_device_view target, size_type target_index, - column_device_view& d_dictionary, + column_device_view source, size_type source_index) const noexcept { - auto const keys = d_dictionary.child(cudf::dictionary_column_view::keys_column_index); - auto const value = keys.element( - static_cast(d_dictionary.element(source_index))); - using Target = target_type_t; - atomicAdd(&target.element(target_index), static_cast(value)); + update_target_element{}( + target, target_index, source, source_index); } - template () || is_fixed_point()>* = nullptr> - __device__ void operator()(mutable_column_device_view& target, + template ()>* = nullptr> + __device__ void operator()(mutable_column_device_view target, size_type target_index, - column_device_view& d_dictionary, - size_type source_index) const noexcept {}; + column_device_view source, + size_type source_index) const noexcept + { + } }; /** - * @brief Specialization function for dictionary type and aggregation SUM. + * @brief Specialization function for dictionary type and aggregations. + * + * The `source` column is a dictionary type. This functor de-references the + * dictionary's keys child column and maps the input source index through + * the dictionary's indices child column to pass to the `update_target_element` + * in the above `update_target_from_dictionary` using the type-dispatcher to + * resolve the keys column type. + * + * `update_target_element( target, target_index, source.keys(), source.indices()[source_index] )` * * @tparam target_has_nulls Indicates presence of null elements in `target` * @tparam source_has_nulls Indicates presence of null elements in `source`. */ -template -struct update_target_element { +template +struct update_target_element< + dictionary32, + k, + target_has_nulls, + source_has_nulls, + std::enable_if_t> { __device__ void operator()(mutable_column_device_view target, size_type target_index, column_device_view source, @@ -299,14 +305,14 @@ struct update_target_element{}, + target, + target_index, + source.child(cudf::dictionary_column_view::keys_column_index), + static_cast(source.element(source_index))); } }; @@ -336,55 +342,6 @@ struct update_target_element()>* = nullptr> - __device__ void operator()(mutable_column_device_view& target, - size_type target_index, - column_device_view& d_dictionary, - size_type source_index) const noexcept - { - auto const keys = d_dictionary.child(cudf::dictionary_column_view::keys_column_index); - auto const value = keys.element( - static_cast(d_dictionary.element(source_index))); - using Target = target_type_t; - atomicAdd(&target.element(target_index), static_cast(value * value)); - } - template ()>* = nullptr> - __device__ void operator()(mutable_column_device_view& target, - size_type target_index, - column_device_view& d_dictionary, - size_type source_index) const noexcept {}; -}; - -template -struct update_target_element { - __device__ void operator()(mutable_column_device_view target, - size_type target_index, - column_device_view source, - size_type source_index) const noexcept - { - if (source_has_nulls and source.is_null(source_index)) { return; } - - type_dispatcher(source.child(cudf::dictionary_column_view::keys_column_index).type(), - update_target_from_dictionary_squares{}, - target, - target_index, - source, - source_index); - - if (target_has_nulls and target.is_null(target_index)) { target.set_valid(target_index); } - } -}; - template struct update_target_element struct groupby_argmax_test : public cudf::test::BaseFixture { }; +using K = int32_t; TYPED_TEST_CASE(groupby_argmax_test, cudf::test::FixedWidthTypes); -// clang-format off TYPED_TEST(groupby_argmax_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - if (std::is_same::value) return; + if (std::is_same::value) return; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals({9, 8, 7, 6, 5, 4, 3, 2, 1, 0}); + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - fixed_width_column_wrapper expect_vals { 0, 1, 2 }; + fixed_width_column_wrapper expect_keys{1, 2, 3}; + fixed_width_column_wrapper expect_vals{0, 1, 2}; - auto agg = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_argmax_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - if (std::is_same::value) return; + if (std::is_same::value) return; - fixed_width_column_wrapper keys ( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals({3, 4, 5}); + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals({3, 4, 5}); - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_argmax_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - if (std::is_same::value) return; + if (std::is_same::value) return; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals({3, 4, 5}, all_null()); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_argmax_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - if (std::is_same::value) return; + if (std::is_same::value) return; - fixed_width_column_wrapper keys({ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1}); - fixed_width_column_wrapper vals({9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 4}, - {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0}); + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1}); + fixed_width_column_wrapper vals({9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 4}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0}); - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 6, 3, 5, 4, 0, 2, 1, -} - fixed_width_column_wrapper expect_vals({ 3, 4, 7, 0}, - { 1, 1, 1, 0}); + // {1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // {6, 3, 5, 4, 0, 2, 1, -} + fixed_width_column_wrapper expect_vals({3, 4, 7, 0}, {1, 1, 1, 0}); - auto agg = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } - -struct groupby_argmax_string_test : public cudf::test::BaseFixture {}; +struct groupby_argmax_string_test : public cudf::test::BaseFixture { +}; TEST_F(groupby_argmax_string_test, basic) { - using K = int32_t; - using V = string_view; - using R = cudf::detail::target_type_t; + using V = string_view; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2 }; - strings_column_wrapper vals { "año", "bit", "₹1", "aaa", "zit", "bat", "aab", "$1", "€1", "wut"}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + strings_column_wrapper vals{"año", "bit", "₹1", "aaa", "zit", "bat", "aab", "$1", "€1", "wut"}; - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - fixed_width_column_wrapper expect_vals({ 0, 4, 2 }); + fixed_width_column_wrapper expect_keys{1, 2, 3}; + fixed_width_column_wrapper expect_vals({0, 4, 2}); - auto agg = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TEST_F(groupby_argmax_string_test, zero_valid_values) { - using K = int32_t; - using V = string_view; - using R = cudf::detail::target_type_t; + using V = string_view; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - strings_column_wrapper vals ( { "año", "bit", "₹1"}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + strings_column_wrapper vals({"año", "bit", "₹1"}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmax_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmax_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } -// clang-format on - struct groupby_dictionary_argmax_test : public cudf::test::BaseFixture { }; TEST_F(groupby_dictionary_argmax_test, basic) { - using K = int32_t; using V = std::string; using R = cudf::detail::target_type_t; diff --git a/cpp/tests/groupby/group_argmin_test.cpp b/cpp/tests/groupby/group_argmin_test.cpp index 28f13a3b813..18ff0f8fef5 100644 --- a/cpp/tests/groupby/group_argmin_test.cpp +++ b/cpp/tests/groupby/group_argmin_test.cpp @@ -27,154 +27,145 @@ namespace test { template struct groupby_argmin_test : public cudf::test::BaseFixture { }; +using K = int32_t; TYPED_TEST_CASE(groupby_argmin_test, cudf::test::FixedWidthTypes); -// clang-format off TYPED_TEST(groupby_argmin_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - if (std::is_same::value) return; + if (std::is_same::value) return; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals({9, 8, 7, 6, 5, 4, 3, 2, 1, 0}); + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - fixed_width_column_wrapper expect_vals { 6, 9, 8 }; + fixed_width_column_wrapper expect_keys{1, 2, 3}; + fixed_width_column_wrapper expect_vals{6, 9, 8}; - auto agg = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_argmin_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - if (std::is_same::value) return; + if (std::is_same::value) return; - fixed_width_column_wrapper keys ( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals({3, 4, 5}); + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals({3, 4, 5}); - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_argmin_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - if (std::is_same::value) return; + if (std::is_same::value) return; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals({3, 4, 5}, all_null()); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_argmin_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - if (std::is_same::value) return; + if (std::is_same::value) return; - fixed_width_column_wrapper keys({ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals({ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 4}, - { 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0}); + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 4}, + {1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0}); - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 9, 6, 8, 5, 0, 7, 1, -} - fixed_width_column_wrapper expect_vals({ 3, 9, 8, 0}, - { 1, 1, 1, 0}); + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 9, 6, 8, 5, 0, 7, 1, -} + fixed_width_column_wrapper expect_vals({3, 9, 8, 0}, {1, 1, 1, 0}); - auto agg = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - // TODO: explore making this a gtest parameter - auto agg2 = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + // TODO: explore making this a gtest parameter + auto agg2 = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } - -struct groupby_argmin_string_test : public cudf::test::BaseFixture {}; +struct groupby_argmin_string_test : public cudf::test::BaseFixture { +}; TEST_F(groupby_argmin_string_test, basic) { - using K = int32_t; - using V = string_view; - using R = cudf::detail::target_type_t; + using V = string_view; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2 }; - strings_column_wrapper vals { "año", "bit", "₹1", "aaa", "zit", "bat", "aab", "$1", "€1", "wut"}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + strings_column_wrapper vals{"año", "bit", "₹1", "aaa", "zit", "bat", "aab", "$1", "€1", "wut"}; - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - fixed_width_column_wrapper expect_vals({ 3, 5, 7 }); + fixed_width_column_wrapper expect_keys{1, 2, 3}; + fixed_width_column_wrapper expect_vals({3, 5, 7}); - auto agg = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TEST_F(groupby_argmin_string_test, zero_valid_values) { - using K = int32_t; - using V = string_view; - using R = cudf::detail::target_type_t; + using V = string_view; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - strings_column_wrapper vals ( { "año", "bit", "₹1"}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + strings_column_wrapper vals({"año", "bit", "₹1"}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_argmin_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_argmin_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } -// clang-format on struct groupby_dictionary_argmin_test : public cudf::test::BaseFixture { }; TEST_F(groupby_dictionary_argmin_test, basic) { - using K = int32_t; using V = std::string; using R = cudf::detail::target_type_t; // clang-format off - fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2 }; - dictionary_column_wrapper vals{ "año", "bit", "₹1", "aaa", "zit", "bat", "aab", "$1", "€1", "wut"}; + fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2 }; + dictionary_column_wrapper vals{"año", "bit", "₹1", "aaa", "zit", "bat", "aab", "$1", "€1", "wut"}; fixed_width_column_wrapper expect_keys({ 1, 2, 3 }); fixed_width_column_wrapper expect_vals({ 3, 5, 7 }); // clang-format on diff --git a/cpp/tests/groupby/group_count_test.cpp b/cpp/tests/groupby/group_count_test.cpp index 5147e150c58..f3d73a0fe30 100644 --- a/cpp/tests/groupby/group_count_test.cpp +++ b/cpp/tests/groupby/group_count_test.cpp @@ -27,143 +27,141 @@ namespace test { template struct groupby_count_test : public cudf::test::BaseFixture { }; +using K = int32_t; TYPED_TEST_CASE(groupby_count_test, cudf::test::AllTypes); -// clang-format off TYPED_TEST(groupby_count_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - fixed_width_column_wrapper expect_vals { 3, 4, 3 }; + fixed_width_column_wrapper expect_keys{1, 2, 3}; + fixed_width_column_wrapper expect_vals{3, 4, 3}; - auto agg = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg1 = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); + auto agg1 = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); - auto agg2 = cudf::make_count_aggregation(null_policy::INCLUDE); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2)); + auto agg2 = cudf::make_count_aggregation(null_policy::INCLUDE); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2)); } TYPED_TEST(groupby_count_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals; - auto agg = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg1 = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); + auto agg1 = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); } TYPED_TEST(groupby_count_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals { 3, 4, 5}; + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals{3, 4, 5}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg1 = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); + auto agg1 = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); - auto agg2 = cudf::make_count_aggregation(null_policy::INCLUDE); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2)); + auto agg2 = cudf::make_count_aggregation(null_policy::INCLUDE); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2)); } TYPED_TEST(groupby_count_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals ( { 3, 4, 5}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals { 0 }; + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals{0}; - auto agg = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg1 = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); + auto agg1 = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); - fixed_width_column_wrapper expect_vals2 { 3 }; - auto agg2 = cudf::make_count_aggregation(null_policy::INCLUDE); - test_single_agg(keys, vals, expect_keys, expect_vals2, std::move(agg2)); + fixed_width_column_wrapper expect_vals2{3}; + auto agg2 = cudf::make_count_aggregation(null_policy::INCLUDE); + test_single_agg(keys, vals, expect_keys, expect_vals2, std::move(agg2)); } TYPED_TEST(groupby_count_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys({ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, - { 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); + using V = TypeParam; + using R = cudf::detail::target_type_t; - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, -} - fixed_width_column_wrapper expect_vals { 2, 3, 2, 0}; + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); - auto agg = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + // clang-format off + // {1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // {3, 6, 1, 4, 9, 2, 8, -} + fixed_width_column_wrapper expect_vals({2, 3, 2, 0}); + // clang-format on - auto agg1 = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); + auto agg = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - fixed_width_column_wrapper expect_vals2{ 3, 4, 2, 1}; - auto agg2 = cudf::make_count_aggregation(null_policy::INCLUDE); - test_single_agg(keys, vals, expect_keys, expect_vals2, std::move(agg2)); + auto agg1 = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); + fixed_width_column_wrapper expect_vals2{3, 4, 2, 1}; + auto agg2 = cudf::make_count_aggregation(null_policy::INCLUDE); + test_single_agg(keys, vals, expect_keys, expect_vals2, std::move(agg2)); } -struct groupby_count_string_test : public cudf::test::BaseFixture {}; +struct groupby_count_string_test : public cudf::test::BaseFixture { +}; TEST_F(groupby_count_string_test, basic) { - using K = int32_t; - using V = cudf::string_view; - using R = cudf::detail::target_type_t; + using V = cudf::string_view; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 3, 3, 5, 5, 0}; - strings_column_wrapper vals { "1", "1", "1", "1", "1", "1"}; + // clang-format off + fixed_width_column_wrapper keys{1, 3, 3, 5, 5, 0}; + strings_column_wrapper vals{"1", "1", "1", "1", "1", "1"}; + // clang-format on - fixed_width_column_wrapper expect_keys { 0, 1, 3, 5}; - fixed_width_column_wrapper expect_vals { 1, 1, 2, 2}; + fixed_width_column_wrapper expect_keys{0, 1, 3, 5}; + fixed_width_column_wrapper expect_vals{1, 1, 2, 2}; - auto agg = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg1 = cudf::make_count_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); + auto agg1 = cudf::make_count_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg1), force_use_sort_impl::YES); } // clang-format on @@ -180,7 +178,6 @@ TYPED_TEST(FixedPointTestBothReps, GroupByCount) using RepType = cudf::device_storage_type_t; using fp_wrapper = cudf::test::fixed_point_column_wrapper; - using K = int32_t; using V = decimalXX; using R = cudf::detail::target_type_t; @@ -189,7 +186,7 @@ TYPED_TEST(FixedPointTestBothReps, GroupByCount) auto const vals = fp_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; auto const expect_keys = fixed_width_column_wrapper{1, 2, 3}; - auto const expect_vals = fixed_width_column_wrapper{3, 4, 3}; + auto const expect_vals = fixed_width_column_wrapper{3, 4, 3}; auto agg = cudf::make_count_aggregation(); test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); @@ -206,15 +203,14 @@ struct groupby_dictionary_count_test : public cudf::test::BaseFixture { TEST_F(groupby_dictionary_count_test, basic) { - using K = int32_t; using V = std::string; using R = cudf::detail::target_type_t; // clang-format off - strings_column_wrapper keys{"1", "3", "3", "5", "5", "0"}; - dictionary_column_wrapper vals{ 1, 1, 1, 1, 1, 1}; - strings_column_wrapper expect_keys{"0", "1", "3", "5"}; - fixed_width_column_wrapper expect_vals{ 1, 1, 2, 2}; + strings_column_wrapper keys{"1", "3", "3", "5", "5", "0"}; + dictionary_column_wrapper vals{1, 1, 1, 1, 1, 1}; + strings_column_wrapper expect_keys{"0", "1", "3", "5"}; + fixed_width_column_wrapper expect_vals{1, 1, 2, 2}; // clang-format on test_single_agg(keys, vals, expect_keys, expect_vals, cudf::make_count_aggregation()); diff --git a/cpp/tests/groupby/group_max_test.cpp b/cpp/tests/groupby/group_max_test.cpp index 0a13510a948..30720998fe0 100644 --- a/cpp/tests/groupby/group_max_test.cpp +++ b/cpp/tests/groupby/group_max_test.cpp @@ -29,153 +29,141 @@ template struct groupby_max_test : public cudf::test::BaseFixture { }; +using K = int32_t; TYPED_TEST_CASE(groupby_max_test, cudf::test::FixedWidthTypesWithoutFixedPoint); -// clang-format off TYPED_TEST(groupby_max_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - fixed_width_column_wrapper expect_vals({ 6, 9, 8 }); + fixed_width_column_wrapper expect_keys{1, 2, 3}; + fixed_width_column_wrapper expect_vals({6, 9, 8}); - auto agg = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_max_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals { }; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals{}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_max_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals({3, 4, 5}); + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals({3, 4, 5}); - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_max_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals({3, 4, 5}, all_null()); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_max_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys({ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 0, 3, 1, 4, 5, 2, 8, -} - fixed_width_column_wrapper expect_vals({ 3, 5, 8, 0}, - { 1, 1, 1, 0}); - - auto agg = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - - auto agg2 = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); -} + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0}); + + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 0, 3, 1, 4, 5, 2, 8, -} + fixed_width_column_wrapper expect_vals({3, 5, 8, 0}, {1, 1, 1, 0}); + + auto agg = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg2 = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); +} -struct groupby_max_string_test : public cudf::test::BaseFixture {}; +struct groupby_max_string_test : public cudf::test::BaseFixture { +}; TEST_F(groupby_max_string_test, basic) { - using K = int32_t; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + strings_column_wrapper vals{"año", "bit", "₹1", "aaa", "zit", "bat", "aaa", "$1", "₹1", "wut"}; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2 }; - strings_column_wrapper vals { "año", "bit", "₹1", "aaa", "zit", "bat", "aaa", "$1", "₹1", "wut"}; + fixed_width_column_wrapper expect_keys{1, 2, 3}; + strings_column_wrapper expect_vals({"año", "zit", "₹1"}); - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - strings_column_wrapper expect_vals({ "año", "zit", "₹1" }); + auto agg = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - - auto agg2 = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TEST_F(groupby_max_string_test, zero_valid_values) { - using K = int32_t; - - fixed_width_column_wrapper keys { 1, 1, 1}; - strings_column_wrapper vals ( { "año", "bit", "₹1"}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + strings_column_wrapper vals({"año", "bit", "₹1"}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - strings_column_wrapper expect_vals({ "" }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + strings_column_wrapper expect_vals({""}, all_null()); - auto agg = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_max_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_max_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } -// clang-format on struct groupby_dictionary_max_test : public cudf::test::BaseFixture { }; TEST_F(groupby_dictionary_max_test, basic) { - using K = int32_t; using V = std::string; // clang-format off @@ -212,8 +200,10 @@ TYPED_TEST(FixedPointTestBothReps, GroupBySortMaxDecimalAsValue) for (auto const i : {2, 1, 0, -1, -2}) { auto const scale = scale_type{i}; + // clang-format off auto const keys = fixed_width_column_wrapper{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - auto const vals = fp_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + auto const vals = fp_wrapper{ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + // clang-format on auto const expect_keys = fixed_width_column_wrapper{1, 2, 3}; auto const expect_vals_max = fp_wrapper{{6, 9, 8}, scale}; @@ -224,9 +214,7 @@ TYPED_TEST(FixedPointTestBothReps, GroupBySortMaxDecimalAsValue) } } -// This test will not work until the following ptxas bug is fixed in 10.2 -// https://nvbugswb.nvidia.com/NvBugs5/SWBug.aspx?bugid=3186317&cp= -TYPED_TEST(FixedPointTestBothReps, DISABLED_GroupByHashMaxDecimalAsValue) +TYPED_TEST(FixedPointTestBothReps, GroupByHashMaxDecimalAsValue) { using namespace numeric; using decimalXX = TypeParam; @@ -236,8 +224,10 @@ TYPED_TEST(FixedPointTestBothReps, DISABLED_GroupByHashMaxDecimalAsValue) for (auto const i : {2, 1, 0, -1, -2}) { auto const scale = scale_type{i}; + // clang-format off auto const keys = fixed_width_column_wrapper{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - auto const vals = fp_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + auto const vals = fp_wrapper{ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + // clang-format on auto const expect_keys = fixed_width_column_wrapper{1, 2, 3}; auto const expect_vals_max = fp_wrapper{{6, 9, 8}, scale}; diff --git a/cpp/tests/groupby/group_mean_test.cpp b/cpp/tests/groupby/group_mean_test.cpp index 37a8ce4e2d6..026d999e172 100644 --- a/cpp/tests/groupby/group_mean_test.cpp +++ b/cpp/tests/groupby/group_mean_test.cpp @@ -46,95 +46,94 @@ using supported_types = cudf::test::Concat, cudf::test::DurationTypes>; TYPED_TEST_CASE(groupby_mean_test, supported_types); +using K = int32_t; -// clang-format off TYPED_TEST(groupby_mean_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - using RT = typename std::conditional(), int, double>::type; + using V = TypeParam; + using R = cudf::detail::target_type_t; + using RT = typename std::conditional(), int, double>::type; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - std::vector expect_v = convert({ 3., 19./4, 17./3}); - fixed_width_column_wrapper expect_vals(expect_v.cbegin(), expect_v.cend()); + // clang-format off + fixed_width_column_wrapper expect_keys{1, 2, 3}; + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + std::vector expect_v = convert( {3., 19. / 4, 17. / 3}); + fixed_width_column_wrapper expect_vals(expect_v.cbegin(), expect_v.cend()); + // clang-format on - auto agg = cudf::make_mean_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_mean_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_mean_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals { }; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals{}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_mean_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_mean_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_mean_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys ( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals { 3, 4, 5}; + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals{3, 4, 5}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_mean_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_mean_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_mean_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals ( { 3, 4, 5}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_mean_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_mean_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_mean_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - using RT = typename std::conditional(), int, double>::type; - - fixed_width_column_wrapper keys( { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, - { 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, -} - std::vector expect_v = convert({ 4.5, 14./3, 5., 0.}); - fixed_width_column_wrapper expect_vals(expect_v.cbegin(), expect_v.cend(), - { 1, 1, 1, 0}); - - auto agg = cudf::make_mean_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + using RT = typename std::conditional(), int, double>::type; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); + + // clang-format off + // {1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // {3, 6, 1, 4, 9, 2, 8, -} + std::vector expect_v = convert( {4.5, 14. / 3, 5., 0.}); + fixed_width_column_wrapper expect_vals(expect_v.cbegin(), expect_v.cend(), {1, 1, 1, 0}); + // clang-format on + + auto agg = cudf::make_mean_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } // clang-format on @@ -143,16 +142,15 @@ struct groupby_dictionary_mean_test : public cudf::test::BaseFixture { TEST_F(groupby_dictionary_mean_test, basic) { - using K = int32_t; using V = int16_t; using R = cudf::detail::target_type_t; // clang-format off - fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - dictionary_column_wrapper vals{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + dictionary_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - fixed_width_column_wrapper expect_keys({ 1, 2, 3 }); - fixed_width_column_wrapper expect_vals({ 9./3, 19./4, 17./3}); + fixed_width_column_wrapper expect_keys( {1, 2, 3}); + fixed_width_column_wrapper expect_vals({9. / 3, 19. / 4, 17. / 3}); // clang-format on test_single_agg(keys, vals, expect_keys, expect_vals, cudf::make_mean_aggregation()); diff --git a/cpp/tests/groupby/group_median_test.cpp b/cpp/tests/groupby/group_median_test.cpp index 28acadf9ecc..d83e9ec946b 100644 --- a/cpp/tests/groupby/group_median_test.cpp +++ b/cpp/tests/groupby/group_median_test.cpp @@ -28,102 +28,96 @@ template struct groupby_median_test : public cudf::test::BaseFixture { }; +using K = int32_t; using supported_types = cudf::test::Types; TYPED_TEST_CASE(groupby_median_test, supported_types); -// clang-format off TYPED_TEST(groupby_median_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals({ 3., 4.5, 7. }, all_valid()); + // clang-format off + // {1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys{1, 2, 3}; + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({3., 4.5, 7.}, all_valid()); + // clang-format on - auto agg = cudf::make_median_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_median_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_median_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals { }; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals{}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_median_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_median_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_median_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys ( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals { 3, 4, 5}; + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals{3, 4, 5}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_median_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_median_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_median_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals ( { 3, 4, 5}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_median_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_median_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_median_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys( { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, - { 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, -} - fixed_width_column_wrapper expect_vals({ 4.5, 4., 5., 0.}, - { 1, 1, 1, 0}); - - auto agg = cudf::make_median_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); + + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6, 1, 4, 9, 2, 8, -} + fixed_width_column_wrapper expect_vals({4.5, 4., 5., 0.}, {1, 1, 1, 0}); + + auto agg = cudf::make_median_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } -// clang-format on TYPED_TEST(groupby_median_test, dictionary) { - using K = int32_t; using V = TypeParam; using R = cudf::detail::target_type_t; @@ -131,10 +125,10 @@ TYPED_TEST(groupby_median_test, dictionary) fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; dictionary_column_wrapper vals{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys({ 1, 2, 3 }); - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals({ 3., 4.5, 7. }, all_valid()); + // {1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys({1, 2, 3 }); + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({3., 4.5, 7. }, all_valid()); // clang-format on test_single_agg(keys, vals, expect_keys, expect_vals, cudf::make_median_aggregation()); diff --git a/cpp/tests/groupby/group_min_test.cpp b/cpp/tests/groupby/group_min_test.cpp index 4cd0c9864ad..f6340a4838b 100644 --- a/cpp/tests/groupby/group_min_test.cpp +++ b/cpp/tests/groupby/group_min_test.cpp @@ -29,153 +29,141 @@ template struct groupby_min_test : public cudf::test::BaseFixture { }; +using K = int32_t; TYPED_TEST_CASE(groupby_min_test, cudf::test::FixedWidthTypesWithoutFixedPoint); -// clang-format off TYPED_TEST(groupby_min_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - fixed_width_column_wrapper expect_vals({0, 1, 2 }); + fixed_width_column_wrapper expect_keys{1, 2, 3}; + fixed_width_column_wrapper expect_vals({0, 1, 2}); - auto agg = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_min_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals { }; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals{}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_min_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals({3, 4, 5}); + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals({3, 4, 5}); - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_min_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals({3, 4, 5}, all_null()); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_min_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys({ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, - {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, -} - fixed_width_column_wrapper expect_vals({ 3, 1, 2, 0}, - { 1, 1, 1, 0}); + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6, 1, 4, 9, 2, 8, -} + fixed_width_column_wrapper expect_vals({3, 1, 2, 0}, {1, 1, 1, 0}); - auto agg = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } - -struct groupby_min_string_test : public cudf::test::BaseFixture {}; +struct groupby_min_string_test : public cudf::test::BaseFixture { +}; TEST_F(groupby_min_string_test, basic) { - using K = int32_t; - - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2 }; - strings_column_wrapper vals { "año", "bit", "₹1", "aaa", "zit", "bat", "aaa", "$1", "₹1", "wut"}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + strings_column_wrapper vals{"año", "bit", "₹1", "aaa", "zit", "bat", "aaa", "$1", "₹1", "wut"}; - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - strings_column_wrapper expect_vals({ "aaa", "bat", "$1" }); + fixed_width_column_wrapper expect_keys{1, 2, 3}; + strings_column_wrapper expect_vals({"aaa", "bat", "$1"}); - auto agg = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TEST_F(groupby_min_string_test, zero_valid_values) { - using K = int32_t; + fixed_width_column_wrapper keys{1, 1, 1}; + strings_column_wrapper vals({"año", "bit", "₹1"}, all_null()); - fixed_width_column_wrapper keys { 1, 1, 1}; - strings_column_wrapper vals ( { "año", "bit", "₹1"}, all_null() ); + fixed_width_column_wrapper expect_keys{1}; + strings_column_wrapper expect_vals({""}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - strings_column_wrapper expect_vals({ "" }, all_null()); + auto agg = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - - auto agg2 = cudf::make_min_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_min_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } -// clang-format on struct groupby_dictionary_min_test : public cudf::test::BaseFixture { }; TEST_F(groupby_dictionary_min_test, basic) { - using K = int32_t; using V = std::string; // clang-format off @@ -209,12 +197,12 @@ TYPED_TEST(FixedPointTestBothReps, GroupBySortMinDecimalAsValue) using RepType = cudf::device_storage_type_t; using fp_wrapper = cudf::test::fixed_point_column_wrapper; - using K = int32_t; - for (auto const i : {2, 1, 0, -1, -2}) { auto const scale = scale_type{i}; + // clang-format off auto const keys = fixed_width_column_wrapper{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - auto const vals = fp_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + auto const vals = fp_wrapper{ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + // clang-format on auto const expect_keys = fixed_width_column_wrapper{1, 2, 3}; auto const expect_vals_min = fp_wrapper{{0, 1, 2}, scale}; @@ -225,9 +213,7 @@ TYPED_TEST(FixedPointTestBothReps, GroupBySortMinDecimalAsValue) } } -// This test will not work until the following ptxas bug is fixed in 10.2 -// https://nvbugswb.nvidia.com/NvBugs5/SWBug.aspx?bugid=3186317&cp= -TYPED_TEST(FixedPointTestBothReps, DISABLED_GroupByHashMinDecimalAsValue) +TYPED_TEST(FixedPointTestBothReps, GroupByHashMinDecimalAsValue) { using namespace numeric; using decimalXX = TypeParam; @@ -237,8 +223,10 @@ TYPED_TEST(FixedPointTestBothReps, DISABLED_GroupByHashMinDecimalAsValue) for (auto const i : {2, 1, 0, -1, -2}) { auto const scale = scale_type{i}; + // clang-format off auto const keys = fixed_width_column_wrapper{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - auto const vals = fp_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + auto const vals = fp_wrapper{ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + // clang-format on auto const expect_keys = fixed_width_column_wrapper{1, 2, 3}; auto const expect_vals_min = fp_wrapper{{0, 1, 2}, scale}; diff --git a/cpp/tests/groupby/group_nunique_test.cpp b/cpp/tests/groupby/group_nunique_test.cpp index 192e2971fe2..acfa1c953e2 100644 --- a/cpp/tests/groupby/group_nunique_test.cpp +++ b/cpp/tests/groupby/group_nunique_test.cpp @@ -28,195 +28,183 @@ template struct groupby_nunique_test : public cudf::test::BaseFixture { }; +using K = int32_t; TYPED_TEST_CASE(groupby_nunique_test, cudf::test::AllTypes); -// clang-format off TYPED_TEST(groupby_nunique_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals( - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); - - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - fixed_width_column_wrapper expect_vals { 3, 4, 3 }; - fixed_width_column_wrapper expect_bool_vals { 2, 1, 1 }; - - auto agg = cudf::make_nunique_aggregation(); - if(std::is_same()) - test_single_agg(keys, vals, expect_keys, expect_bool_vals, std::move(agg)); - else - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + // clang-format off + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_keys{1, 2, 3}; + fixed_width_column_wrapper expect_vals{3, 4, 3}; + fixed_width_column_wrapper expect_bool_vals{2, 1, 1}; + // clang-format on + + auto agg = cudf::make_nunique_aggregation(); + if (std::is_same()) + test_single_agg(keys, vals, expect_keys, expect_bool_vals, std::move(agg)); + else + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_nunique_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals { }; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals{}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_nunique_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_nunique_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_nunique_test, basic_duplicates) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 3, 2, 2, 9}); - - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - fixed_width_column_wrapper expect_vals { 2, 4, 1 }; - fixed_width_column_wrapper expect_bool_vals { 2, 1, 1 }; - - auto agg = cudf::make_nunique_aggregation(); - if(std::is_same()) - test_single_agg(keys, vals, expect_keys, expect_bool_vals, std::move(agg)); - else - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 3, 2, 2, 9}; + + fixed_width_column_wrapper expect_keys{1, 2, 3}; + fixed_width_column_wrapper expect_vals{2, 4, 1}; + fixed_width_column_wrapper expect_bool_vals{2, 1, 1}; + + auto agg = cudf::make_nunique_aggregation(); + if (std::is_same()) + test_single_agg(keys, vals, expect_keys, expect_bool_vals, std::move(agg)); + else + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_nunique_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals({3, 4, 5}); + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals({3, 4, 5}); - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_nunique_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_nunique_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_nunique_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals({3, 4, 5}, all_null()); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals { 0 }; + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals{0}; - auto agg = cudf::make_nunique_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_nunique_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_nunique_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys({ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, - {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // all unique values only // { 3, 6, 1, 4, 9, 2, 8, -} - fixed_width_column_wrapper expect_vals { 2, 3, 2, 0}; - fixed_width_column_wrapper expect_bool_vals { 1, 1, 1, 0}; - - - auto agg = cudf::make_nunique_aggregation(); - if(std::is_same()) - test_single_agg(keys, vals, expect_keys, expect_bool_vals, std::move(agg)); - else - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); + + // {1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // all unique values only {3, 6, 1, 4, 9, 2, 8, -} + fixed_width_column_wrapper expect_vals{2, 3, 2, 0}; + fixed_width_column_wrapper expect_bool_vals{1, 1, 1, 0}; + + auto agg = cudf::make_nunique_aggregation(); + if (std::is_same()) + test_single_agg(keys, vals, expect_keys, expect_bool_vals, std::move(agg)); + else + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_nunique_test, null_keys_and_values_with_duplicates) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys({ 1, 2, 3, 3, 1, 2, 2, 1, 3, 3, 2, 4, 4, 2}, - { 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); - fixed_width_column_wrapper vals({0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 4, 4, 2}, - {0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6,- 1, 4, 9,- 2*, 8, -*} - // unique, with null, dup, dup null - fixed_width_column_wrapper expect_vals { 2, 3, 2, 0}; - fixed_width_column_wrapper expect_bool_vals { 1, 1, 1, 0}; - - - auto agg = cudf::make_nunique_aggregation(); - if(std::is_same()) - test_single_agg(keys, vals, expect_keys, expect_bool_vals, std::move(agg)); - else - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); -} + using V = TypeParam; + using R = cudf::detail::target_type_t; + fixed_width_column_wrapper keys({1, 2, 3, 3, 1, 2, 2, 1, 3, 3, 2, 4, 4, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 4, 4, 2}, + {0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0}); + + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6,- 1, 4, 9,- 2*, 8, -*} + // unique, with null, dup, dup null + fixed_width_column_wrapper expect_vals{2, 3, 2, 0}; + fixed_width_column_wrapper expect_bool_vals{1, 1, 1, 0}; + + auto agg = cudf::make_nunique_aggregation(); + if (std::is_same()) + test_single_agg(keys, vals, expect_keys, expect_bool_vals, std::move(agg)); + else + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); +} TYPED_TEST(groupby_nunique_test, include_nulls) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys({ 1, 2, 3, 3, 1, 2, 2, 1, 3, 3, 2, 4, 4, 2}, - { 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); - fixed_width_column_wrapper vals({0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 4, 4, 2}, - {0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6,- 1, 4, 9,- 2*, 8, -*} - // unique, with null, dup, dup null - fixed_width_column_wrapper expect_vals { 3, 4, 2, 1}; - fixed_width_column_wrapper expect_bool_vals { 2, 2, 1, 1}; - - - auto agg = cudf::make_nunique_aggregation(null_policy::INCLUDE); - if(std::is_same()) - test_single_agg(keys, vals, expect_keys, expect_bool_vals, std::move(agg)); - else - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 3, 1, 2, 2, 1, 3, 3, 2, 4, 4, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 4, 4, 2}, + {0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0}); + + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6,- 1, 4, 9,- 2*, 8, -*} + // unique, with null, dup, dup null + fixed_width_column_wrapper expect_vals{3, 4, 2, 1}; + fixed_width_column_wrapper expect_bool_vals{2, 2, 1, 1}; + + auto agg = cudf::make_nunique_aggregation(null_policy::INCLUDE); + if (std::is_same()) + test_single_agg(keys, vals, expect_keys, expect_bool_vals, std::move(agg)); + else + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } -// clang-format on TYPED_TEST(groupby_nunique_test, dictionary) { - using K = int32_t; using V = TypeParam; using R = cudf::detail::target_type_t; // clang-format off - fixed_width_column_wrapper keys({1, 2, 3, 3, 1, 2, 2, 1, 0, 3, 2, 4, 4, 2}, - {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); - dictionary_column_wrapper vals({0, 1, 2, 2, 3, 4, 0, 6, 7, 8, 9, 0, 0, 0}, - {0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); - // { 3, 6,- 1, 4, 9,- 2*, 8, -*} - // unique, with null, dup, dup null - fixed_width_column_wrapper expect_fixed_vals({3, 4, 2, 1}); - fixed_width_column_wrapper expect_bool_vals { 2, 2, 1, 1}; + fixed_width_column_wrapper keys({1, 2, 3, 3, 1, 2, 2, 1, 0, 3, 2, 4, 4, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); + dictionary_column_wrapper vals({0, 1, 2, 2, 3, 4, 0, 6, 7, 8, 9, 0, 0, 0}, + {0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0}); + + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6,- 1, 4, 9,- 2*, 8, -*} + // unique, with null, dup, dup null + fixed_width_column_wrapper expect_fixed_vals({3, 4, 2, 1}); + fixed_width_column_wrapper expect_bool_vals{2, 2, 1, 1}; // clang-format on cudf::column_view expect_vals = (std::is_same()) ? cudf::column_view{expect_bool_vals} diff --git a/cpp/tests/groupby/group_product_test.cpp b/cpp/tests/groupby/group_product_test.cpp index 5af27585bee..d2db409711d 100644 --- a/cpp/tests/groupby/group_product_test.cpp +++ b/cpp/tests/groupby/group_product_test.cpp @@ -114,16 +114,14 @@ TYPED_TEST(groupby_product_test, null_keys_and_values) test_single_agg(keys, vals, expect_keys, expect_vals, cudf::make_product_aggregation()); } -// This test will not work until the following ptxas bug is fixed in 10.2 -// https://nvbugswb.nvidia.com/NvBugs5/SWBug.aspx?bugid=3186317&cp= -TYPED_TEST(groupby_product_test, DISABLED_dictionary) +TYPED_TEST(groupby_product_test, dictionary) { using V = TypeParam; using R = cudf::detail::target_type_t; // clang-format off fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - dictionary_column_wrapper vals{ 0, 2, 2, 3, 4, 5, 6, 7, 8, 9}; + dictionary_column_wrapper vals{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} fixed_width_column_wrapper expect_keys({ 1, 2, 3 }); @@ -134,5 +132,24 @@ TYPED_TEST(groupby_product_test, DISABLED_dictionary) test_single_agg(keys, vals, expect_keys, expect_vals, cudf::make_product_aggregation()); } +TYPED_TEST(groupby_product_test, dictionary_with_nulls) +{ + using V = TypeParam; + using R = cudf::detail::target_type_t; + + // clang-format off + fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + dictionary_column_wrapper vals{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {1, 0, 0, 1, 1, 1, 1, 1, 1, 1}}; + + // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys({ 1, 2, 3 }); + // { 0, 3, 6, @, 4, 5, 9, @, 7, 8} + fixed_width_column_wrapper expect_vals({ 0., 180., 56. }, all_valid()); + // clang-format on + + test_single_agg(keys, vals, expect_keys, expect_vals, cudf::make_product_aggregation()); +} + } // namespace test } // namespace cudf diff --git a/cpp/tests/groupby/group_quantile_test.cpp b/cpp/tests/groupby/group_quantile_test.cpp index 9908423d088..babd84d4334 100644 --- a/cpp/tests/groupby/group_quantile_test.cpp +++ b/cpp/tests/groupby/group_quantile_test.cpp @@ -30,181 +30,163 @@ struct groupby_quantile_test : public cudf::test::BaseFixture { using supported_types = cudf::test::Types; +using K = int32_t; TYPED_TEST_CASE(groupby_quantile_test, supported_types); -// clang-format off TYPED_TEST(groupby_quantile_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals({ 3., 4.5, 7. }, all_valid()); + // clang-format on + // {1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys{1, 2, 3}; + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({3., 4.5, 7.}, all_valid()); + // clang-format on - auto agg = cudf::make_quantile_aggregation({0.5}, - interpolation::LINEAR); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_quantile_aggregation({0.5}, interpolation::LINEAR); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_quantile_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals { }; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals{}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_quantile_aggregation({0.5}, - interpolation::LINEAR); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_quantile_aggregation({0.5}, interpolation::LINEAR); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_quantile_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys ( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals { 3, 4, 5}; + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals{3, 4, 5}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_quantile_aggregation({0.5}, - interpolation::LINEAR); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_quantile_aggregation({0.5}, interpolation::LINEAR); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_quantile_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals ( { 3, 4, 5}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_quantile_aggregation({0.5}, - interpolation::LINEAR); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_quantile_aggregation({0.5}, interpolation::LINEAR); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_quantile_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys( { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, - { 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, -} - fixed_width_column_wrapper expect_vals({ 4.5, 4., 5., 0.}, - { 1, 1, 1, 0}); - - auto agg = cudf::make_quantile_aggregation({0.5}, - interpolation::LINEAR); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); + + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6, 1, 4, 9, 2, 8, -} + fixed_width_column_wrapper expect_vals({4.5, 4., 5., 0.}, {1, 1, 1, 0}); + + auto agg = cudf::make_quantile_aggregation({0.5}, interpolation::LINEAR); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_quantile_test, multiple_quantile) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals({ 1.5,4.5, 3.25, 6., 4.5,7.5}, all_valid()); - - auto agg = cudf::make_quantile_aggregation({0.25, 0.75}, - interpolation::LINEAR); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg), - force_use_sort_impl::YES); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + // clang-format off + // {1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys{1, 2, 3}; + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({1.5, 4.5, 3.25, 6., 4.5, 7.5}, all_valid()); + // clang-format on + + auto agg = cudf::make_quantile_aggregation({0.25, 0.75}, interpolation::LINEAR); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg), force_use_sort_impl::YES); } TYPED_TEST(groupby_quantile_test, interpolation_types) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 2}; - fixed_width_column_wrapper vals { 0, 1, 2, 3, 4, 5, 6, 7, 9}; - - // { 1, 1, 1, 2, 2, 2, 2, 3, 3} - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - - - // { 0, 3, 6, 1, 4, 5, 9, 2, 7} - fixed_width_column_wrapper expect_vals1({ 2.4, 4.2, 4. }, all_valid()); - auto agg1 = cudf::make_quantile_aggregation({0.4}, - interpolation::LINEAR); - test_single_agg(keys, vals, expect_keys, expect_vals1, std::move(agg1)); - - // { 0, 3, 6, 1, 4, 5, 9, 2, 7} - fixed_width_column_wrapper expect_vals2({ 3, 4, 2 }, all_valid()); - auto agg2 = cudf::make_quantile_aggregation({0.4}, - interpolation::NEAREST); - test_single_agg(keys, vals, expect_keys, expect_vals2, std::move(agg2)); - - // { 0, 3, 6, 1, 4, 5, 9, 2, 7} - fixed_width_column_wrapper expect_vals3({ 0, 4, 2 }, all_valid()); - auto agg3 = cudf::make_quantile_aggregation({0.4}, - interpolation::LOWER); - test_single_agg(keys, vals, expect_keys, expect_vals3, std::move(agg3)); - - // { 0, 3, 6, 1, 4, 5, 9, 2, 7} - fixed_width_column_wrapper expect_vals4({ 3, 5, 7 }, all_valid()); - auto agg4 = cudf::make_quantile_aggregation({0.4}, - interpolation::HIGHER); - test_single_agg(keys, vals, expect_keys, expect_vals4, std::move(agg4)); - - // { 0, 3, 6, 1, 4, 5, 9, 2, 7} - fixed_width_column_wrapper expect_vals5({ 1.5, 4.5, 4.5}, all_valid()); - auto agg5 = cudf::make_quantile_aggregation({0.4}, - interpolation::MIDPOINT); - test_single_agg(keys, vals, expect_keys, expect_vals5, std::move(agg5)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 9}; + + // clang-format off + // {1, 1, 1, 2, 2, 2, 2, 3, 3} + fixed_width_column_wrapper expect_keys{1, 2, 3}; + + // {0, 3, 6, 1, 4, 5, 9, 2, 7} + fixed_width_column_wrapper expect_vals1({2.4, 4.2, 4.}, all_valid()); + auto agg1 = cudf::make_quantile_aggregation({0.4}, interpolation::LINEAR); + test_single_agg(keys, vals, expect_keys, expect_vals1, std::move(agg1)); + + // {0, 3, 6, 1, 4, 5, 9, 2, 7} + fixed_width_column_wrapper expect_vals2({3, 4, 2}, all_valid()); + auto agg2 = cudf::make_quantile_aggregation({0.4}, interpolation::NEAREST); + test_single_agg(keys, vals, expect_keys, expect_vals2, std::move(agg2)); + + // {0, 3, 6, 1, 4, 5, 9, 2, 7} + fixed_width_column_wrapper expect_vals3({0, 4, 2}, all_valid()); + auto agg3 = cudf::make_quantile_aggregation({0.4}, interpolation::LOWER); + test_single_agg(keys, vals, expect_keys, expect_vals3, std::move(agg3)); + + // {0, 3, 6, 1, 4, 5, 9, 2, 7} + fixed_width_column_wrapper expect_vals4({3, 5, 7}, all_valid()); + auto agg4 = cudf::make_quantile_aggregation({0.4}, interpolation::HIGHER); + test_single_agg(keys, vals, expect_keys, expect_vals4, std::move(agg4)); + + // {0, 3, 6, 1, 4, 5, 9, 2, 7} + fixed_width_column_wrapper expect_vals5({1.5, 4.5, 4.5}, all_valid()); + auto agg5 = cudf::make_quantile_aggregation({0.4}, interpolation::MIDPOINT); + test_single_agg(keys, vals, expect_keys, expect_vals5, std::move(agg5)); + // clang-format on } -// clang-format on TYPED_TEST(groupby_quantile_test, dictionary) { - using K = int32_t; using V = TypeParam; using R = cudf::detail::target_type_t; // clang-format off - fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - dictionary_column_wrapper vals{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + dictionary_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys({ 1, 2, 3 }); - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals({ 3., 4.5, 7. }, all_valid()); + // {1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys({1, 2, 3}); + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({3., 4.5, 7.}, all_valid()); // clang-format on test_single_agg(keys, diff --git a/cpp/tests/groupby/group_std_test.cpp b/cpp/tests/groupby/group_std_test.cpp index 7cd034d309f..f9980f3f5a6 100644 --- a/cpp/tests/groupby/group_std_test.cpp +++ b/cpp/tests/groupby/group_std_test.cpp @@ -30,134 +30,127 @@ template struct groupby_std_test : public cudf::test::BaseFixture { }; +using K = int32_t; using supported_types = cudf::test::Types; TYPED_TEST_CASE(groupby_std_test, supported_types); -// clang-format off TYPED_TEST(groupby_std_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals({ 3., sqrt(131./12),sqrt(31./3)}, all_valid()); + // clang-format off + // {1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys{1, 2, 3}; + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({3., sqrt(131./12), sqrt(31./3)}, all_valid()); + // clang-format on - auto agg = cudf::make_std_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_std_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_std_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals { }; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals{}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_std_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_std_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_std_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys ( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals { 3, 4, 5}; + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals{3, 4, 5}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_std_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_std_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_std_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals ( { 3, 4, 5}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_std_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_std_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_std_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys( { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3}, - { 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, 3} - fixed_width_column_wrapper expect_vals({3/sqrt(2), 7/sqrt(3), 3*sqrt(2), 0.}, - { 1, 1, 1, 0}); - - auto agg = cudf::make_std_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); + + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6, 1, 4, 9, 2, 8, 3} + fixed_width_column_wrapper expect_vals({3 / sqrt(2), 7 / sqrt(3), 3 * sqrt(2), 0.}, + {1, 1, 1, 0}); + + auto agg = cudf::make_std_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_std_test, ddof_non_default) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys( { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3}, - { 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, 3} - fixed_width_column_wrapper expect_vals({ 0., 7*sqrt(2./3), 0., 0.}, - { 0, 1, 0, 0}); - - auto agg = cudf::make_std_aggregation(2); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); + + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6, 1, 4, 9, 2, 8, 3} + fixed_width_column_wrapper expect_vals({0., 7 * sqrt(2. / 3), 0., 0.}, {0, 1, 0, 0}); + + auto agg = cudf::make_std_aggregation(2); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } -// clang-format on TYPED_TEST(groupby_std_test, dictionary) { - using K = int32_t; using V = TypeParam; using R = cudf::detail::target_type_t; // clang-format off - fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - dictionary_column_wrapper vals{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + dictionary_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys({ 1, 2, 3 }); - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals({ 3., sqrt(131./12),sqrt(31./3)}, all_valid()); + // {1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys({1, 2, 3}); + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({3., sqrt(131./12), sqrt(31./3)}, all_valid()); // clang-format on test_single_agg(keys, vals, expect_keys, expect_vals, cudf::make_std_aggregation()); diff --git a/cpp/tests/groupby/group_sum_of_squares_test.cpp b/cpp/tests/groupby/group_sum_of_squares_test.cpp index aad1df1bf27..24306a51056 100644 --- a/cpp/tests/groupby/group_sum_of_squares_test.cpp +++ b/cpp/tests/groupby/group_sum_of_squares_test.cpp @@ -28,116 +28,105 @@ template struct groupby_sum_of_squares_test : public cudf::test::BaseFixture { }; -// These tests will not work for all types until the following ptxas bug is fixed in 10.2 -// https://nvbugswb.nvidia.com/NvBugs5/SWBug.aspx?bugid=3186317&cp= -// using supported_types = cudf::test::Types; -using supported_types = cudf::test::Types; +using supported_types = cudf::test::Types; +using K = int32_t; TYPED_TEST_CASE(groupby_sum_of_squares_test, supported_types); -// clang-format off TYPED_TEST(groupby_sum_of_squares_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals({ 45., 123., 117. }, all_valid()); + // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys{1, 2, 3}; + // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({45., 123., 117.}, all_valid()); - auto agg = cudf::make_sum_of_squares_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_sum_of_squares_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_sum_of_squares_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals { }; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals{}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_sum_of_squares_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_sum_of_squares_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_sum_of_squares_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys ( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals { 3, 4, 5}; + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals{3, 4, 5}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_sum_of_squares_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_sum_of_squares_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_sum_of_squares_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals ( { 3, 4, 5}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_sum_of_squares_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_sum_of_squares_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_sum_of_squares_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys( { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3}, - { 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, 3} - fixed_width_column_wrapper expect_vals({ 45., 98., 68., 9.}, - { 1, 1, 1, 0}); - - auto agg = cudf::make_sum_of_squares_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); + + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6, 1, 4, 9, 2, 8, 3} + fixed_width_column_wrapper expect_vals({45., 98., 68., 9.}, {1, 1, 1, 0}); + + auto agg = cudf::make_sum_of_squares_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } -// clang-format on TYPED_TEST(groupby_sum_of_squares_test, dictionary) { - using K = int32_t; using V = TypeParam; using R = cudf::detail::target_type_t; // clang-format off - fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - dictionary_column_wrapper vals{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + dictionary_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys({ 1, 2, 3 }); - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals( { 45., 123., 117. }, all_valid()); + // {1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys({1, 2, 3 }); + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({45., 123., 117. }, all_valid()); // clang-format on test_single_agg(keys, vals, expect_keys, expect_vals, cudf::make_sum_of_squares_aggregation()); diff --git a/cpp/tests/groupby/group_sum_test.cpp b/cpp/tests/groupby/group_sum_test.cpp index 662634d9450..90544dd0db6 100644 --- a/cpp/tests/groupby/group_sum_test.cpp +++ b/cpp/tests/groupby/group_sum_test.cpp @@ -28,126 +28,119 @@ template struct groupby_sum_test : public cudf::test::BaseFixture { }; +using K = int32_t; using supported_types = cudf::test::Concat, cudf::test::DurationTypes>; TYPED_TEST_CASE(groupby_sum_test, supported_types); -// clang-format off TYPED_TEST(groupby_sum_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - fixed_width_column_wrapper expect_vals { 9, 19, 17}; + fixed_width_column_wrapper expect_keys{1, 2, 3}; + fixed_width_column_wrapper expect_vals{9, 19, 17}; - auto agg = cudf::make_sum_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_sum_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_sum_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_sum_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_sum_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals { }; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals{}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_sum_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_sum_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_sum_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_sum_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_sum_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys ( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals { 3, 4, 5}; + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals{3, 4, 5}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_sum_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_sum_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_sum_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_sum_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_sum_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals ( { 3, 4, 5}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_sum_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_sum_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - auto agg2 = cudf::make_sum_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + auto agg2 = cudf::make_sum_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } TYPED_TEST(groupby_sum_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys( { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, - { 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, -} - fixed_width_column_wrapper expect_vals({ 9, 14, 10, 0}, - { 1, 1, 1, 0}); - - auto agg = cudf::make_sum_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); - - auto agg2 = cudf::make_sum_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}); + + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6, 1, 4, 9, 2, 8, -} + fixed_width_column_wrapper expect_vals({9, 14, 10, 0}, {1, 1, 1, 0}); + + auto agg = cudf::make_sum_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + + auto agg2 = cudf::make_sum_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg2), force_use_sort_impl::YES); } // clang-format on TYPED_TEST(groupby_sum_test, dictionary) { - using K = int32_t; using V = TypeParam; using R = cudf::detail::target_type_t; // clang-format off - fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - dictionary_column_wrapper vals{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + dictionary_column_wrapper vals{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - fixed_width_column_wrapper expect_keys{ 1, 2, 3 }; - fixed_width_column_wrapper expect_vals{ 9, 19, 17}; + fixed_width_column_wrapper expect_keys{ 1, 2, 3 }; + fixed_width_column_wrapper expect_vals{ 9, 19, 17}; // clang-format on test_single_agg(keys, vals, expect_keys, expect_vals, cudf::make_sum_aggregation()); @@ -172,8 +165,10 @@ TYPED_TEST(FixedPointTestBothReps, GroupBySortSumDecimalAsValue) for (auto const i : {2, 1, 0, -1, -2}) { auto const scale = scale_type{i}; + // clang-format off auto const keys = fixed_width_column_wrapper{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - auto const vals = fp_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + auto const vals = fp_wrapper{ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + // clang-format on auto const expect_keys = fixed_width_column_wrapper{1, 2, 3}; auto const expect_vals_sum = fp64_wrapper{{9, 19, 17}, scale}; @@ -189,9 +184,7 @@ TYPED_TEST(FixedPointTestBothReps, GroupBySortSumDecimalAsValue) } } -// This test will not work until the following ptxas bug is fixed in 10.2 -// https://nvbugswb.nvidia.com/NvBugs5/SWBug.aspx?bugid=3186317&cp= -TYPED_TEST(FixedPointTestBothReps, DISABLED_GroupByHashSumDecimalAsValue) +TYPED_TEST(FixedPointTestBothReps, GroupByHashSumDecimalAsValue) { using namespace numeric; using decimalXX = TypeParam; @@ -202,8 +195,10 @@ TYPED_TEST(FixedPointTestBothReps, DISABLED_GroupByHashSumDecimalAsValue) for (auto const i : {2, 1, 0, -1, -2}) { auto const scale = scale_type{i}; + // clang-format off auto const keys = fixed_width_column_wrapper{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - auto const vals = fp_wrapper{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + auto const vals = fp_wrapper{ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, scale}; + // clang-format on auto const expect_keys = fixed_width_column_wrapper{1, 2, 3}; auto const expect_vals_sum = fp64_wrapper{{9, 19, 17}, scale}; diff --git a/cpp/tests/groupby/group_var_test.cpp b/cpp/tests/groupby/group_var_test.cpp index 62d6beaa906..5835d850b8c 100644 --- a/cpp/tests/groupby/group_var_test.cpp +++ b/cpp/tests/groupby/group_var_test.cpp @@ -29,135 +29,132 @@ namespace test { template struct groupby_var_test : public cudf::test::BaseFixture { }; +using K = int32_t; using supported_types = cudf::test::Types; TYPED_TEST_CASE(groupby_var_test, supported_types); -// clang-format off TYPED_TEST(groupby_var_test, basic) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - fixed_width_column_wrapper vals { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + // clang-format off + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + fixed_width_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys { 1, 2, 3 }; - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals({ 9., 131./12, 31./3 }, all_valid()); + // {1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys{1, 2, 3}; + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({9., 131. / 12, 31. / 3}, all_valid()); + // clang-format on - auto agg = cudf::make_variance_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_variance_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_var_test, empty_cols) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { }; - fixed_width_column_wrapper vals { }; + fixed_width_column_wrapper keys{}; + fixed_width_column_wrapper vals{}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_variance_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_variance_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_var_test, zero_valid_keys) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys ( { 1, 2, 3}, all_null() ); - fixed_width_column_wrapper vals { 3, 4, 5}; + fixed_width_column_wrapper keys({1, 2, 3}, all_null()); + fixed_width_column_wrapper vals{3, 4, 5}; - fixed_width_column_wrapper expect_keys { }; - fixed_width_column_wrapper expect_vals { }; + fixed_width_column_wrapper expect_keys{}; + fixed_width_column_wrapper expect_vals{}; - auto agg = cudf::make_variance_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_variance_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_var_test, zero_valid_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; + using V = TypeParam; + using R = cudf::detail::target_type_t; - fixed_width_column_wrapper keys { 1, 1, 1}; - fixed_width_column_wrapper vals ( { 3, 4, 5}, all_null() ); + fixed_width_column_wrapper keys{1, 1, 1}; + fixed_width_column_wrapper vals({3, 4, 5}, all_null()); - fixed_width_column_wrapper expect_keys { 1 }; - fixed_width_column_wrapper expect_vals({ 0 }, all_null()); + fixed_width_column_wrapper expect_keys{1}; + fixed_width_column_wrapper expect_vals({0}, all_null()); - auto agg = cudf::make_variance_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + auto agg = cudf::make_variance_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_var_test, null_keys_and_values) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys( { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3}, - { 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, 3} - fixed_width_column_wrapper expect_vals({ 4.5, 49./3, 18., 0.}, - { 1, 1, 1, 0}); - - auto agg = cudf::make_variance_aggregation(); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); + + // clang-format off + // {1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // {3, 6, 1, 4, 9, 2, 8, 3} + fixed_width_column_wrapper expect_vals({4.5, 49. / 3, 18., 0.}, {1, 1, 1, 0}); + // clang-format on + + auto agg = cudf::make_variance_aggregation(); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } TYPED_TEST(groupby_var_test, ddof_non_default) { - using K = int32_t; - using V = TypeParam; - using R = cudf::detail::target_type_t; - - fixed_width_column_wrapper keys( { 1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, - { 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); - fixed_width_column_wrapper vals( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3}, - { 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); - - // { 1, 1, 2, 2, 2, 3, 3, 4} - fixed_width_column_wrapper expect_keys({ 1, 2, 3, 4}, all_valid()); - // { 3, 6, 1, 4, 9, 2, 8, 3} - fixed_width_column_wrapper expect_vals({ 0., 98./3, 0., 0.}, - { 0, 1, 0, 0}); - - auto agg = cudf::make_variance_aggregation(2); - test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); + using V = TypeParam; + using R = cudf::detail::target_type_t; + + fixed_width_column_wrapper keys({1, 2, 3, 1, 2, 2, 1, 3, 3, 2, 4}, + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1}); + fixed_width_column_wrapper vals({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3}, + {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}); + + // clang-format off + // { 1, 1, 2, 2, 2, 3, 3, 4} + fixed_width_column_wrapper expect_keys({1, 2, 3, 4}, all_valid()); + // { 3, 6, 1, 4, 9, 2, 8, 3} + fixed_width_column_wrapper expect_vals({0., 98. / 3, 0., 0.}, + {0, 1, 0, 0}); + // clang-format on + + auto agg = cudf::make_variance_aggregation(2); + test_single_agg(keys, vals, expect_keys, expect_vals, std::move(agg)); } -// clang-format on TYPED_TEST(groupby_var_test, dictionary) { - using K = int32_t; using V = TypeParam; using R = cudf::detail::target_type_t; // clang-format off - fixed_width_column_wrapper keys{ 1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; - dictionary_column_wrapper vals{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + fixed_width_column_wrapper keys{1, 2, 3, 1, 2, 2, 1, 3, 3, 2}; + dictionary_column_wrapper vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - // { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3} - fixed_width_column_wrapper expect_keys({ 1, 2, 3 }); - // { 0, 3, 6, 1, 4, 5, 9, 2, 7, 8} - fixed_width_column_wrapper expect_vals( { 9., 131./12, 31./3 }, all_valid()); + // {1, 1, 1, 2, 2, 2, 2, 3, 3, 3} + fixed_width_column_wrapper expect_keys({1, 2, 3 }); + // {0, 3, 6, 1, 4, 5, 9, 2, 7, 8} + fixed_width_column_wrapper expect_vals({9., 131./12, 31./3 }, all_valid()); // clang-format on test_single_agg(keys, vals, expect_keys, expect_vals, cudf::make_variance_aggregation()); From cf8c73aed29179b3165a92a10faac6ca45ccd6d0 Mon Sep 17 00:00:00 2001 From: "Robert (Bobby) Evans" Date: Sat, 1 May 2021 17:43:07 -0500 Subject: [PATCH 15/65] Some APIs to help with out of core joins in Spark (#8118) It adds a simple prefix sum for doing size calculations. It also changes a few APIs so that they take a ColumnView instead of a ColumnVector. Authors: - Robert (Bobby) Evans (https://github.com/revans2) Approvers: - Jason Lowe (https://github.com/jlowe) URL: https://github.com/rapidsai/cudf/pull/8118 --- .../main/java/ai/rapids/cudf/ColumnView.java | 14 ++++++ java/src/main/java/ai/rapids/cudf/Table.java | 12 ++--- java/src/main/native/CMakeLists.txt | 1 + java/src/main/native/src/ColumnViewJni.cpp | 16 +++++++ java/src/main/native/src/prefix_sum.cu | 48 +++++++++++++++++++ java/src/main/native/src/prefix_sum.hpp | 36 ++++++++++++++ .../java/ai/rapids/cudf/ColumnVectorTest.java | 28 +++++++++++ 7 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 java/src/main/native/src/prefix_sum.cu create mode 100644 java/src/main/native/src/prefix_sum.hpp diff --git a/java/src/main/java/ai/rapids/cudf/ColumnView.java b/java/src/main/java/ai/rapids/cudf/ColumnView.java index f6f4891cdcb..51f89ea1114 100644 --- a/java/src/main/java/ai/rapids/cudf/ColumnView.java +++ b/java/src/main/java/ai/rapids/cudf/ColumnView.java @@ -1312,6 +1312,18 @@ public final ColumnVector rollingWindow(Aggregation op, WindowOptions options) { } } + /** + * Compute the cumulative sum/prefix sum of the values in this column. + * This is similar to a rolling window SUM with unbounded preceding and none following. + * Input values 1, 2, 3 + * Output values 1, 3, 6 + * This currently only works for long values that are not nullable as this is currently a + * very simple implementation. It may be expanded in the future if needed. + */ + public final ColumnVector prefixSum() { + return new ColumnVector(prefixSum(getNativeView())); + } + ///////////////////////////////////////////////////////////////////////////// // LOGICAL ///////////////////////////////////////////////////////////////////////////// @@ -2919,6 +2931,8 @@ private static native long rollingWindow( long preceding_col, long following_col); + private static native long prefixSum(long viewHandle) throws CudfException; + private static native long nansToNulls(long viewHandle) throws CudfException; private static native long charLengths(long viewHandle) throws CudfException; diff --git a/java/src/main/java/ai/rapids/cudf/Table.java b/java/src/main/java/ai/rapids/cudf/Table.java index ea04e615bb6..a4fe3acab08 100644 --- a/java/src/main/java/ai/rapids/cudf/Table.java +++ b/java/src/main/java/ai/rapids/cudf/Table.java @@ -1261,7 +1261,7 @@ public Table repeat(int count) { * @return the new Table. * @throws CudfException on any error. */ - public Table repeat(ColumnVector counts) { + public Table repeat(ColumnView counts) { return repeat(counts, true); } @@ -1276,7 +1276,7 @@ public Table repeat(ColumnVector counts) { * @return the new Table. * @throws CudfException on any error. */ - public Table repeat(ColumnVector counts, boolean checkCount) { + public Table repeat(ColumnView counts, boolean checkCount) { return new Table(repeatColumnCount(this.nativeHandle, counts.getNativeView(), checkCount)); } @@ -1719,7 +1719,7 @@ private int[] copyAndValidate(int[] indices) { * @return table containing copy of all elements of this table passing * the filter defined by the boolean mask */ - public Table filter(ColumnVector mask) { + public Table filter(ColumnView mask) { assert mask.getType().equals(DType.BOOL8) : "Mask column must be of type BOOL8"; assert getRowCount() == 0 || getRowCount() == mask.getRowCount() : "Mask column has incorrect size"; return new Table(filter(nativeHandle, mask.getNativeView())); @@ -1955,7 +1955,7 @@ public ColumnVector rowBitCount() { * @param gatherMap the map of indexes. Must be non-nullable and integral type. * @return the resulting Table. */ - public Table gather(ColumnVector gatherMap) { + public Table gather(ColumnView gatherMap) { return gather(gatherMap, true); } @@ -1973,7 +1973,7 @@ public Table gather(ColumnVector gatherMap) { * when setting this to false. * @return the resulting Table. */ - public Table gather(ColumnVector gatherMap, boolean checkBounds) { + public Table gather(ColumnView gatherMap, boolean checkBounds) { return new Table(gather(nativeHandle, gatherMap.getNativeView(), checkBounds)); } @@ -2191,7 +2191,7 @@ public ColumnVector[] convertToRows() { * @param schema the types of each column. * @return the parsed table. */ - public static Table convertFromRows(ColumnVector vec, DType ... schema) { + public static Table convertFromRows(ColumnView vec, DType ... schema) { // TODO at some point we need a schema that support nesting so we can support nested types // TODO we will need scale at some point very soon too int[] types = new int[schema.length]; diff --git a/java/src/main/native/CMakeLists.txt b/java/src/main/native/CMakeLists.txt index 17776288b49..179a6936d8b 100755 --- a/java/src/main/native/CMakeLists.txt +++ b/java/src/main/native/CMakeLists.txt @@ -248,6 +248,7 @@ set(SOURCE_FILES "src/RmmJni.cpp" "src/ScalarJni.cpp" "src/TableJni.cpp" + "src/prefix_sum.cu" "src/map_lookup.cu") add_library(cudfjni SHARED ${SOURCE_FILES}) diff --git a/java/src/main/native/src/ColumnViewJni.cpp b/java/src/main/native/src/ColumnViewJni.cpp index cec3a1a92a6..c9bafa5abee 100644 --- a/java/src/main/native/src/ColumnViewJni.cpp +++ b/java/src/main/native/src/ColumnViewJni.cpp @@ -64,6 +64,7 @@ #include #include "cudf/types.hpp" +#include "prefix_sum.hpp" #include "cudf_jni_apis.hpp" #include "dtype_utils.hpp" #include "jni.h" @@ -1755,6 +1756,20 @@ JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_ColumnView_makeStructView(JNIEnv *en CATCH_STD(env, 0); } +JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_ColumnView_prefixSum(JNIEnv *env, jobject j_object, + jlong handle) { + + JNI_NULL_CHECK(env, handle, "native view handle is null", 0) + + try { + cudf::jni::auto_set_device(env); + cudf::column_view *view = reinterpret_cast(handle); + std::unique_ptr result = cudf::jni::prefix_sum(*view); + return reinterpret_cast(result.release()); + } + CATCH_STD(env, 0) +} + JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_ColumnView_nansToNulls(JNIEnv *env, jobject j_object, jlong handle) { @@ -1779,6 +1794,7 @@ JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_ColumnView_nansToNulls(JNIEnv *env, CATCH_STD(env, 0) } + JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_ColumnView_isFloat(JNIEnv *env, jobject j_object, jlong handle) { diff --git a/java/src/main/native/src/prefix_sum.cu b/java/src/main/native/src/prefix_sum.cu new file mode 100644 index 00000000000..e3c53696185 --- /dev/null +++ b/java/src/main/native/src/prefix_sum.cu @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include +#include +#include + + +namespace cudf { +namespace jni { + +std::unique_ptr prefix_sum(column_view const &value_column, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource *mr) { + // Defensive checks. + CUDF_EXPECTS(value_column.type().id() == type_id::INT64, "Only longs are supported."); + CUDF_EXPECTS(!value_column.has_nulls(), "NULLS are not supported"); + + auto result = make_numeric_column(value_column.type(), value_column.size(), + mask_state::ALL_VALID, stream, mr); + + thrust::inclusive_scan(rmm::exec_policy(stream), + value_column.begin(), + value_column.end(), + result->mutable_view().begin()); + + return result; +} +} // namespace jni +} // namespace cudf diff --git a/java/src/main/native/src/prefix_sum.hpp b/java/src/main/native/src/prefix_sum.hpp new file mode 100644 index 00000000000..8f39f9a8c69 --- /dev/null +++ b/java/src/main/native/src/prefix_sum.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace cudf { + +namespace jni { + +/** + * @brief compute the prefix sum of a column of longs + */ +std::unique_ptr +prefix_sum(column_view const &value_column, + rmm::cuda_stream_view stream = rmm::cuda_stream_default, + rmm::mr::device_memory_resource *mr = rmm::mr::get_current_device_resource()); + +} // namespace jni + +} // namespace cudf diff --git a/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java b/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java index cca7090b8c7..a30d276d954 100644 --- a/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java +++ b/java/src/test/java/ai/rapids/cudf/ColumnVectorTest.java @@ -2058,6 +2058,34 @@ void testStringConcatSeparators() { } } + @Test + void testPrefixSum() { + try (ColumnVector v1 = ColumnVector.fromLongs(1, 2, 3, 5, 8, 10); + ColumnVector summed = v1.prefixSum(); + ColumnVector expected = ColumnVector.fromLongs(1, 3, 6, 11, 19, 29)) { + assertColumnsAreEqual(expected, summed); + } + } + + @Test + void testPrefixSumErrors() { + try (ColumnVector v1 = ColumnVector.fromBoxedLongs(1L, 2L, 3L, 5L, 8L, null)) { + assertThrows(CudfException.class, () -> { + try(ColumnVector ignored = v1.prefixSum()) { + // empty + } + }); + } + + try (ColumnVector v1 = ColumnVector.fromInts(1, 2, 3, 5, 8, 10)) { + assertThrows(CudfException.class, () -> { + try(ColumnVector ignored = v1.prefixSum()) { + // empty + } + }); + } + } + @Test void testWindowStatic() { WindowOptions options = WindowOptions.builder().window(2, 1) From 8a4426f550ccd7bbf96b0dc83622a10447b282ed Mon Sep 17 00:00:00 2001 From: Devavret Makkar Date: Mon, 3 May 2021 11:09:54 +0530 Subject: [PATCH 16/65] Use spans in parquet writer (#7950) - Replaced raw pointers in parquet writer with appropriate spans - Used `hostdevice_2dvector` as approprate - Various parameter cleanups Depends on #7758 and #7853 Authors: - Devavret Makkar (https://github.com/devavret) - Mark Harris (https://github.com/harrism) Approvers: - Mike Wilson (https://github.com/hyperbolic2346) - Vukasin Milovanovic (https://github.com/vuule) URL: https://github.com/rapidsai/cudf/pull/7950 --- cpp/include/cudf/utilities/span.hpp | 11 + cpp/src/io/parquet/page_dict.cu | 13 +- cpp/src/io/parquet/page_enc.cu | 267 ++++++++++----------- cpp/src/io/parquet/parquet_gpu.hpp | 141 +++++------ cpp/src/io/parquet/writer_impl.cu | 223 ++++++++--------- cpp/src/io/parquet/writer_impl.hpp | 47 ++-- cpp/src/io/utilities/hostdevice_vector.hpp | 6 + 7 files changed, 311 insertions(+), 397 deletions(-) diff --git a/cpp/include/cudf/utilities/span.hpp b/cpp/include/cudf/utilities/span.hpp index 0575a5c9f13..2f3577623a9 100644 --- a/cpp/include/cudf/utilities/span.hpp +++ b/cpp/include/cudf/utilities/span.hpp @@ -258,6 +258,17 @@ class base_2dspan { return {this->data() + flatten_index(row, 0, this->size()), this->size().second}; } + constexpr base_2dspan subspan(size_t first_row, size_t num_rows) const noexcept + { + return base_2dspan( + _data + flatten_index(first_row, 0, this->size()), num_rows, this->size().second); + } + + constexpr RowType flat_view() + { + return {this->data(), this->size().first * this->size().second}; + } + template typename OtherRowType, diff --git a/cpp/src/io/parquet/page_dict.cu b/cpp/src/io/parquet/page_dict.cu index 30842820448..2d505b99981 100644 --- a/cpp/src/io/parquet/page_dict.cu +++ b/cpp/src/io/parquet/page_dict.cu @@ -150,7 +150,7 @@ __device__ void GenerateDictionaryIndices(dict_state_s *s, uint32_t t) // blockDim(1024, 1, 1) template __global__ void __launch_bounds__(block_size, 1) - gpuBuildChunkDictionaries(EncColumnChunk *chunks, uint32_t *dev_scratch) + gpuBuildChunkDictionaries(device_span chunks, uint32_t *dev_scratch) { __shared__ __align__(8) dict_state_s state_g; using block_reduce = cub::BlockReduce; @@ -321,19 +321,14 @@ __global__ void __launch_bounds__(block_size, 1) * * @param[in,out] chunks Column chunks * @param[in] dev_scratch Device scratch data (kDictScratchSize per dictionary) - * @param[in] num_chunks Number of column chunks * @param[in] stream CUDA stream to use, default 0 */ -void BuildChunkDictionaries(EncColumnChunk *chunks, +void BuildChunkDictionaries(device_span chunks, uint32_t *dev_scratch, - size_t scratch_size, - uint32_t num_chunks, rmm::cuda_stream_view stream) { - if (num_chunks > 0 && scratch_size > 0) { // zero scratch size implies no dictionaries - CUDA_TRY(cudaMemsetAsync(dev_scratch, 0, scratch_size, stream.value())); - gpuBuildChunkDictionaries<1024><<>>(chunks, dev_scratch); - } + auto num_chunks = chunks.size(); + gpuBuildChunkDictionaries<1024><<>>(chunks, dev_scratch); } } // namespace gpu diff --git a/cpp/src/io/parquet/page_enc.cu b/cpp/src/io/parquet/page_enc.cu index 6c31605887a..bf9114949aa 100644 --- a/cpp/src/io/parquet/page_enc.cu +++ b/cpp/src/io/parquet/page_enc.cu @@ -40,6 +40,8 @@ constexpr bool enable_bool_rle = true; constexpr bool enable_bool_rle = false; #endif +using ::cudf::detail::device_2dspan; + constexpr int init_hash_bits = 12; constexpr uint32_t rle_buffer_size = (1 << 9); @@ -72,7 +74,7 @@ struct page_enc_state_s { EncColumnChunk ck; parquet_column_device_view col; gpu_inflate_input_s comp_in; - gpu_inflate_status_s comp_out; + gpu_inflate_status_s comp_stat; uint16_t vals[rle_buffer_size]; }; @@ -114,10 +116,8 @@ inline __device__ uint32_t uint64_init_hash(uint64_t v) // blockDim {512,1,1} template __global__ void __launch_bounds__(block_size) - gpuInitPageFragments(PageFragment *frag, - const parquet_column_device_view *col_desc, - int32_t num_fragments, - int32_t num_columns, + gpuInitPageFragments(device_2dspan frag, + device_span col_desc, uint32_t fragment_size, uint32_t max_num_rows) { @@ -377,42 +377,42 @@ __global__ void __launch_bounds__(block_size) } } __syncthreads(); - if (t == 0) frag[blockIdx.x * num_fragments + blockIdx.y] = s->frag; + if (t == 0) frag[blockIdx.x][blockIdx.y] = s->frag; } // blockDim {128,1,1} __global__ void __launch_bounds__(128) - gpuInitFragmentStats(statistics_group *groups, - const PageFragment *fragments, - const parquet_column_device_view *col_desc, - int32_t num_fragments, - int32_t num_columns, - uint32_t fragment_size) + gpuInitFragmentStats(device_2dspan groups, + device_2dspan fragments, + device_span col_desc) { + // TODO: why not 1 block per warp? __shared__ __align__(8) statistics_group group_g[4]; - uint32_t lane_id = threadIdx.x & 0x1f; - uint32_t frag_id = blockIdx.y * 4 + (threadIdx.x >> 5); - uint32_t column_id = blockIdx.x; - statistics_group *const g = &group_g[threadIdx.x >> 5]; - if (!lane_id && frag_id < num_fragments) { + uint32_t lane_id = threadIdx.x & 0x1f; + uint32_t frag_id = blockIdx.y * 4 + (threadIdx.x >> 5); + uint32_t column_id = blockIdx.x; + auto num_fragments_per_column = fragments.size().second; + statistics_group *const g = &group_g[threadIdx.x >> 5]; + if (!lane_id && frag_id < num_fragments_per_column) { g->col = &col_desc[column_id]; - g->start_row = fragments[column_id * num_fragments + frag_id].start_value_idx; - g->num_rows = fragments[column_id * num_fragments + frag_id].num_leaf_values; + g->start_row = fragments[column_id][frag_id].start_value_idx; + g->num_rows = fragments[column_id][frag_id].num_leaf_values; } __syncthreads(); - if (frag_id < num_fragments and lane_id == 0) groups[column_id * num_fragments + frag_id] = *g; + if (frag_id < num_fragments_per_column and lane_id == 0) groups[column_id][frag_id] = *g; } // blockDim {128,1,1} -__global__ void __launch_bounds__(128) gpuInitPages(EncColumnChunk *chunks, - EncPage *pages, - const parquet_column_device_view *col_desc, - statistics_merge_group *page_grstats, - statistics_merge_group *chunk_grstats, - int32_t num_rowgroups, - int32_t num_columns) +__global__ void __launch_bounds__(128) + gpuInitPages(device_2dspan chunks, + device_span pages, + device_span col_desc, + statistics_merge_group *page_grstats, + statistics_merge_group *chunk_grstats, + int32_t num_columns) { + // TODO: All writing seems to be done by thread 0. Could be replaced by thrust foreach __shared__ __align__(8) parquet_column_device_view col_g; __shared__ __align__(8) EncColumnChunk ck_g; __shared__ __align__(8) PageFragment frag_g; @@ -422,8 +422,9 @@ __global__ void __launch_bounds__(128) gpuInitPages(EncColumnChunk *chunks, uint32_t t = threadIdx.x; if (t == 0) { - col_g = col_desc[blockIdx.x]; - ck_g = chunks[blockIdx.y * num_columns + blockIdx.x]; + col_g = col_desc[blockIdx.x]; + ck_g = chunks[blockIdx.y][blockIdx.x]; + page_g = {}; } __syncthreads(); if (t < 32) { @@ -454,6 +455,7 @@ __global__ void __launch_bounds__(128) gpuInitPages(EncColumnChunk *chunks, page_g.num_fragments = 0; page_g.page_type = PageType::DICTIONARY_PAGE; page_g.dict_bits_plus1 = 0; + page_g.chunk = &chunks[blockIdx.y][blockIdx.x]; page_g.chunk_id = blockIdx.y * num_columns + blockIdx.x; page_g.hdr_size = 0; page_g.max_hdr_size = 32; @@ -467,7 +469,7 @@ __global__ void __launch_bounds__(128) gpuInitPages(EncColumnChunk *chunks, } __syncwarp(); if (t == 0) { - if (pages) pages[ck_g.first_page] = page_g; + if (not pages.empty()) pages[ck_g.first_page] = page_g; if (page_grstats) page_grstats[ck_g.first_page] = pagestats_g; } num_pages = 1; @@ -531,6 +533,7 @@ __global__ void __launch_bounds__(128) gpuInitPages(EncColumnChunk *chunks, } if (!t) { page_g.num_fragments = fragments_in_chunk - page_start; + page_g.chunk = &chunks[blockIdx.y][blockIdx.x]; page_g.chunk_id = blockIdx.y * num_columns + blockIdx.x; page_g.page_type = PageType::DATA_PAGE; page_g.dict_bits_plus1 = dict_bits_plus1; @@ -574,7 +577,7 @@ __global__ void __launch_bounds__(128) gpuInitPages(EncColumnChunk *chunks, } __syncwarp(); if (t == 0) { - if (pages) { pages[ck_g.first_page + num_pages] = page_g; } + if (not pages.empty()) { pages[ck_g.first_page + num_pages] = page_g; } if (page_grstats) { page_grstats[ck_g.first_page + num_pages] = pagestats_g; } } @@ -613,7 +616,8 @@ __global__ void __launch_bounds__(128) gpuInitPages(EncColumnChunk *chunks, } __syncthreads(); if (t == 0) { - chunks[blockIdx.y * num_columns + blockIdx.x] = ck_g; + if (not pages.empty()) ck_g.pages = &pages[ck_g.first_page]; + chunks[blockIdx.y][blockIdx.x] = ck_g; if (chunk_grstats) chunk_grstats[blockIdx.y * num_columns + blockIdx.x] = pagestats_g; } } @@ -922,11 +926,10 @@ convert_nanoseconds(cuda::std::chrono::sys_time // blockDim(128, 1, 1) template -__global__ void __launch_bounds__(128, 8) gpuEncodePages(EncPage *pages, - const EncColumnChunk *chunks, - gpu_inflate_input_s *comp_in, - gpu_inflate_status_s *comp_out, - uint32_t start_page) +__global__ void __launch_bounds__(128, 8) + gpuEncodePages(device_span pages, + device_span comp_in, + device_span comp_stat) { __shared__ __align__(8) page_enc_state_s state_g; using block_scan = cub::BlockScan; @@ -938,8 +941,8 @@ __global__ void __launch_bounds__(128, 8) gpuEncodePages(EncPage *pages, int32_t dict_bits; if (t == 0) { - s->page = pages[start_page + blockIdx.x]; - s->ck = chunks[s->page.chunk_id]; + s->page = pages[blockIdx.x]; + s->ck = *s->page.chunk; s->col = *s->ck.col_desc; s->cur = s->page.page_data + s->page.max_hdr_size; } @@ -1255,49 +1258,53 @@ __global__ void __launch_bounds__(128, 8) gpuEncodePages(EncPage *pages, s->comp_in.srcSize = actual_data_size; s->comp_in.dstDevice = s->page.compressed_data + s->page.max_hdr_size; s->comp_in.dstSize = compressed_bfr_size; - s->comp_out.bytes_written = 0; - s->comp_out.status = ~0; - s->comp_out.reserved = 0; + s->comp_stat.bytes_written = 0; + s->comp_stat.status = ~0; + s->comp_stat.reserved = 0; } __syncthreads(); if (t == 0) { - pages[start_page + blockIdx.x] = s->page; - if (comp_in) comp_in[blockIdx.x] = s->comp_in; - if (comp_out) comp_out[blockIdx.x] = s->comp_out; + pages[blockIdx.x] = s->page; + if (not comp_in.empty()) comp_in[blockIdx.x] = s->comp_in; + if (not comp_stat.empty()) { + comp_stat[blockIdx.x] = s->comp_stat; + pages[blockIdx.x].comp_stat = &comp_stat[blockIdx.x]; + } } } // blockDim(128, 1, 1) -__global__ void __launch_bounds__(128) gpuDecideCompression(EncColumnChunk *chunks, - const EncPage *pages, - const gpu_inflate_status_s *comp_out, - uint32_t start_page) +__global__ void __launch_bounds__(128) gpuDecideCompression(device_span chunks) { + // After changing the way structs are loaded from coop to normal, this kernel has no business + // being launched with 128 thread block. It can easily be a single warp. __shared__ __align__(8) EncColumnChunk ck_g; __shared__ __align__(4) unsigned int error_count; using warp_reduce = cub::WarpReduce; __shared__ typename warp_reduce::TempStorage temp_storage[2]; + __shared__ volatile bool has_compression; uint32_t t = threadIdx.x; uint32_t uncompressed_data_size = 0; uint32_t compressed_data_size = 0; - uint32_t first_page, num_pages; + uint32_t num_pages; if (t == 0) { ck_g = chunks[blockIdx.x]; atomicAnd(&error_count, 0); + has_compression = false; } __syncthreads(); if (t < 32) { - first_page = ck_g.first_page; - num_pages = ck_g.num_pages; + num_pages = ck_g.num_pages; for (uint32_t page = t; page < num_pages; page += 32) { - uint32_t page_data_size = pages[first_page + page].max_data_size; - uint32_t comp_idx = first_page + page - start_page; + auto &curr_page = ck_g.pages[page]; + uint32_t page_data_size = curr_page.max_data_size; uncompressed_data_size += page_data_size; - if (comp_out) { - compressed_data_size += (uint32_t)comp_out[comp_idx].bytes_written; - if (comp_out[comp_idx].status != 0) { atomicAdd(&error_count, 1); } + if (auto comp_status = curr_page.comp_stat; comp_status != nullptr) { + has_compression = true; + compressed_data_size += comp_status->bytes_written; + if (comp_status->status != 0) { atomicAdd(&error_count, 1); } } } uncompressed_data_size = warp_reduce(temp_storage[0]).Sum(uncompressed_data_size); @@ -1306,7 +1313,7 @@ __global__ void __launch_bounds__(128) gpuDecideCompression(EncColumnChunk *chun __syncthreads(); if (t == 0) { bool is_compressed; - if (comp_out) { + if (has_compression) { uint32_t compression_error = atomicAdd(&error_count, 0); is_compressed = (!compression_error && compressed_data_size < uncompressed_data_size); } else { @@ -1427,11 +1434,10 @@ class header_encoder { __device__ uint8_t *EncodeStatistics(uint8_t *start, const statistics_chunk *s, - const parquet_column_device_view *col, + uint8_t dtype, float *fp_scratch) { - uint8_t *end, dtype, dtype_len; - dtype = col->stats_dtype; + uint8_t *end, dtype_len; switch (dtype) { case dtype_bool: dtype_len = 1; break; case dtype_int8: @@ -1478,13 +1484,13 @@ __device__ uint8_t *EncodeStatistics(uint8_t *start, } // blockDim(128, 1, 1) -__global__ void __launch_bounds__(128) gpuEncodePageHeaders(EncPage *pages, - EncColumnChunk *chunks, - const gpu_inflate_status_s *comp_out, - const statistics_chunk *page_stats, - const statistics_chunk *chunk_stats, - uint32_t start_page) +__global__ void __launch_bounds__(128) + gpuEncodePageHeaders(device_span pages, + device_span comp_stat, + device_span page_stats, + const statistics_chunk *chunk_stats) { + // When this whole kernel becomes single thread, the following variables need not be __shared__ __shared__ __align__(8) parquet_column_device_view col_g; __shared__ __align__(8) EncColumnChunk ck_g; __shared__ __align__(8) EncPage page_g; @@ -1496,19 +1502,20 @@ __global__ void __launch_bounds__(128) gpuEncodePageHeaders(EncPage *pages, uint8_t *hdr_start, *hdr_end; uint32_t compressed_page_size, uncompressed_page_size; - page_g = pages[start_page + blockIdx.x]; - ck_g = chunks[page_g.chunk_id]; + page_g = pages[blockIdx.x]; + ck_g = *page_g.chunk; col_g = *ck_g.col_desc; - if (chunk_stats && start_page + blockIdx.x == ck_g.first_page) { + if (chunk_stats && &pages[blockIdx.x] == ck_g.pages) { // Is this the first page in a chunk? hdr_start = (ck_g.is_compressed) ? ck_g.compressed_bfr : ck_g.uncompressed_bfr; - hdr_end = EncodeStatistics(hdr_start, &chunk_stats[page_g.chunk_id], &col_g, fp_scratch); - chunks[page_g.chunk_id].ck_stat_size = static_cast(hdr_end - hdr_start); + hdr_end = + EncodeStatistics(hdr_start, &chunk_stats[page_g.chunk_id], col_g.stats_dtype, fp_scratch); + page_g.chunk->ck_stat_size = static_cast(hdr_end - hdr_start); } uncompressed_page_size = page_g.max_data_size; if (ck_g.is_compressed) { hdr_start = page_g.compressed_data; - compressed_page_size = (uint32_t)comp_out[blockIdx.x].bytes_written; + compressed_page_size = (uint32_t)comp_stat[blockIdx.x].bytes_written; page_g.max_data_size = compressed_page_size; } else { hdr_start = page_g.page_data; @@ -1542,10 +1549,10 @@ __global__ void __launch_bounds__(128) gpuEncodePageHeaders(EncPage *pages, encoder.field_int32(3, Encoding::RLE); // definition_level_encoding encoder.field_int32(4, Encoding::RLE); // repetition_level_encoding // Optionally encode page-level statistics - if (page_stats) { + if (not page_stats.empty()) { encoder.field_struct_begin(5); encoder.set_ptr(EncodeStatistics( - encoder.get_ptr(), &page_stats[start_page + blockIdx.x], &col_g, fp_scratch)); + encoder.get_ptr(), &page_stats[blockIdx.x], col_g.stats_dtype, fp_scratch)); encoder.field_struct_end(5); } encoder.field_struct_end(5); @@ -1560,11 +1567,12 @@ __global__ void __launch_bounds__(128) gpuEncodePageHeaders(EncPage *pages, page_g.hdr_size = (uint32_t)(hdr_end - hdr_start); } __syncthreads(); - if (t == 0) pages[start_page + blockIdx.x] = page_g; + if (t == 0) pages[blockIdx.x] = page_g; } // blockDim(1024, 1, 1) -__global__ void __launch_bounds__(1024) gpuGatherPages(EncColumnChunk *chunks, const EncPage *pages) +__global__ void __launch_bounds__(1024) + gpuGatherPages(device_span chunks, device_span pages) { __shared__ __align__(8) EncColumnChunk ck_g; __shared__ __align__(8) EncPage page_g; @@ -1577,7 +1585,7 @@ __global__ void __launch_bounds__(1024) gpuGatherPages(EncColumnChunk *chunks, c if (t == 0) ck_g = chunks[blockIdx.x]; __syncthreads(); - first_page = &pages[ck_g.first_page]; + first_page = ck_g.pages; num_pages = ck_g.num_pages; dst = (ck_g.is_compressed) ? ck_g.compressed_bfr : ck_g.uncompressed_bfr; dst += ck_g.ck_stat_size; // Skip over chunk statistics @@ -2090,17 +2098,17 @@ dremel_data get_dremel_data(column_view h_col, * @param[in] num_columns Number of columns * @param[in] stream CUDA stream to use, default 0 */ -void InitPageFragments(PageFragment *frag, - const parquet_column_device_view *col_desc, - int32_t num_fragments, - int32_t num_columns, +void InitPageFragments(device_2dspan frag, + device_span col_desc, uint32_t fragment_size, uint32_t num_rows, rmm::cuda_stream_view stream) { - dim3 dim_grid(num_columns, num_fragments); // 1 threadblock per fragment - gpuInitPageFragments<512><<>>( - frag, col_desc, num_fragments, num_columns, fragment_size, num_rows); + auto num_columns = frag.size().first; + auto num_fragments_per_column = frag.size().second; + dim3 dim_grid(num_columns, num_fragments_per_column); // 1 threadblock per fragment + gpuInitPageFragments<512> + <<>>(frag, col_desc, fragment_size, num_rows); } /** @@ -2109,22 +2117,18 @@ void InitPageFragments(PageFragment *frag, * @param[out] groups Statistics groups [num_columns x num_fragments] * @param[in] fragments Page fragments [num_columns x num_fragments] * @param[in] col_desc Column description [num_columns] - * @param[in] num_fragments Number of fragments - * @param[in] num_columns Number of columns - * @param[in] fragment_size Max size of each fragment in rows * @param[in] stream CUDA stream to use, default 0 */ -void InitFragmentStatistics(statistics_group *groups, - const PageFragment *fragments, - const parquet_column_device_view *col_desc, - int32_t num_fragments, - int32_t num_columns, - uint32_t fragment_size, +void InitFragmentStatistics(device_2dspan groups, + device_2dspan fragments, + device_span col_desc, rmm::cuda_stream_view stream) { - dim3 dim_grid(num_columns, (num_fragments + 3) >> 2); // 1 warp per fragment - gpuInitFragmentStats<<>>( - groups, fragments, col_desc, num_fragments, num_columns, fragment_size); + int const num_columns = col_desc.size(); + int const num_fragments_per_column = fragments.size().second; + auto grid_y = util::div_rounding_up_safe(num_fragments_per_column, 128 / cudf::detail::warp_size); + dim3 dim_grid(num_columns, grid_y); // 1 warp per fragment + gpuInitFragmentStats<<>>(groups, fragments, col_desc); } /** @@ -2139,88 +2143,69 @@ void InitFragmentStatistics(statistics_group *groups, * @param[out] chunk_grstats Setup for chunk-level stats * @param[in] stream CUDA stream to use, default 0 */ -void InitEncoderPages(EncColumnChunk *chunks, - EncPage *pages, - const parquet_column_device_view *col_desc, - int32_t num_rowgroups, +void InitEncoderPages(device_2dspan chunks, + device_span pages, + device_span col_desc, int32_t num_columns, statistics_merge_group *page_grstats, statistics_merge_group *chunk_grstats, rmm::cuda_stream_view stream) { + auto num_rowgroups = chunks.size().first; dim3 dim_grid(num_columns, num_rowgroups); // 1 threadblock per rowgroup gpuInitPages<<>>( - chunks, pages, col_desc, page_grstats, chunk_grstats, num_rowgroups, num_columns); + chunks, pages, col_desc, page_grstats, chunk_grstats, num_columns); } /** * @brief Launches kernel for packing column data into parquet pages * * @param[in,out] pages Device array of EncPages (unordered) - * @param[in] chunks Column chunks - * @param[in] num_pages Number of pages - * @param[in] start_page First page to encode in page array * @param[out] comp_in Optionally initializes compressor input params - * @param[out] comp_out Optionally initializes compressor output params + * @param[out] comp_stat Optionally initializes compressor status * @param[in] stream CUDA stream to use, default 0 */ -void EncodePages(EncPage *pages, - const EncColumnChunk *chunks, - uint32_t num_pages, - uint32_t start_page, - gpu_inflate_input_s *comp_in, - gpu_inflate_status_s *comp_out, +void EncodePages(device_span pages, + device_span comp_in, + device_span comp_stat, rmm::cuda_stream_view stream) { + auto num_pages = pages.size(); // A page is part of one column. This is launching 1 block per page. 1 block will exclusively // deal with one datatype. - gpuEncodePages<128> - <<>>(pages, chunks, comp_in, comp_out, start_page); + gpuEncodePages<128><<>>(pages, comp_in, comp_stat); } /** * @brief Launches kernel to make the compressed vs uncompressed chunk-level decision * * @param[in,out] chunks Column chunks - * @param[in] pages Device array of EncPages (unordered) - * @param[in] num_chunks Number of column chunks - * @param[in] start_page First page to encode in page array - * @param[in] comp_out Compressor status * @param[in] stream CUDA stream to use, default 0 */ -void DecideCompression(EncColumnChunk *chunks, - const EncPage *pages, - uint32_t num_chunks, - uint32_t start_page, - const gpu_inflate_status_s *comp_out, - rmm::cuda_stream_view stream) +void DecideCompression(device_span chunks, rmm::cuda_stream_view stream) { - gpuDecideCompression<<>>(chunks, pages, comp_out, start_page); + gpuDecideCompression<<>>(chunks); } /** * @brief Launches kernel to encode page headers * * @param[in,out] pages Device array of EncPages - * @param[in,out] chunks Column chunks - * @param[in] num_pages Number of pages - * @param[in] start_page First page to encode in page array - * @param[in] comp_out Compressor status or nullptr if no compression + * @param[in] comp_stat Compressor status or nullptr if no compression * @param[in] page_stats Optional page-level statistics to be included in page header * @param[in] chunk_stats Optional chunk-level statistics to be encoded * @param[in] stream CUDA stream to use, default 0 */ -void EncodePageHeaders(EncPage *pages, - EncColumnChunk *chunks, - uint32_t num_pages, - uint32_t start_page, - const gpu_inflate_status_s *comp_out, - const statistics_chunk *page_stats, +void EncodePageHeaders(device_span pages, + device_span comp_stat, + device_span page_stats, const statistics_chunk *chunk_stats, rmm::cuda_stream_view stream) { - gpuEncodePageHeaders<<>>( - pages, chunks, comp_out, page_stats, chunk_stats, start_page); + // TODO: single thread task. No need for 128 threads/block. Earlier it used to employ rest of the + // threads to coop load structs + gpuEncodePageHeaders<<>>( + pages, comp_stat, page_stats, chunk_stats); } /** @@ -2228,15 +2213,13 @@ void EncodePageHeaders(EncPage *pages, * * @param[in,out] chunks Column chunks * @param[in] pages Device array of EncPages - * @param[in] num_chunks Number of column chunks * @param[in] stream CUDA stream to use, default 0 */ -void GatherPages(EncColumnChunk *chunks, - const EncPage *pages, - uint32_t num_chunks, +void GatherPages(device_span chunks, + device_span pages, rmm::cuda_stream_view stream) { - gpuGatherPages<<>>(chunks, pages); + gpuGatherPages<<>>(chunks, pages); } } // namespace gpu diff --git a/cpp/src/io/parquet/parquet_gpu.hpp b/cpp/src/io/parquet/parquet_gpu.hpp index a7698ea8a78..564226c7ff3 100644 --- a/cpp/src/io/parquet/parquet_gpu.hpp +++ b/cpp/src/io/parquet/parquet_gpu.hpp @@ -16,16 +16,17 @@ #pragma once -#include -#include -#include -#include -#include "parquet_common.hpp" +#include "io/comp/gpuinflate.h" +#include "io/parquet/parquet_common.hpp" +#include "io/statistics/column_stats.h" +#include "io/utilities/column_buffer.hpp" +#include "io/utilities/hostdevice_vector.hpp" #include #include #include #include +#include #include #include @@ -225,7 +226,6 @@ struct parquet_column_device_view : stats_column_desc { uint32_t *dict_data; //!< Dictionary data (unique row indices) uint8_t physical_type; //!< physical data type uint8_t converted_type; //!< logical data type - // TODO (dm): Evaluate if this is sufficient. At 4 bits, this allows a maximum 16 level nesting uint8_t level_bits; //!< bits to encode max definition (lower nibble) & repetition (upper nibble) //!< levels constexpr uint8_t num_def_level_bits() { return level_bits & 0xf; } @@ -258,26 +258,6 @@ struct PageFragment { uint16_t num_dict_vals; //!< Number of unique dictionary entries }; -/** - * @brief Struct describing an encoder data page - */ -struct EncPage { - uint8_t *page_data; //!< Ptr to uncompressed page - uint8_t *compressed_data; //!< Ptr to compressed page - uint16_t num_fragments; //!< Number of fragments in page - PageType page_type; //!< Page type - uint8_t dict_bits_plus1; //!< 0=plain, nonzero:bits to encoding dictionary indices + 1 - uint32_t chunk_id; //!< Index in chunk array - uint32_t hdr_size; //!< Size of page header - uint32_t max_hdr_size; //!< Maximum size of page header - uint32_t max_data_size; //!< Maximum size of coded page data (excluding header) - uint32_t start_row; //!< First row of page - uint32_t num_rows; //!< Rows in page - uint32_t num_leaf_values; //!< Values in page. Different from num_rows in case of nested types - uint32_t num_values; //!< Number of def/rep level values in page. Includes null/empty elements in - //!< non-leaf levels -}; - /// Size of hash used for building dictionaries constexpr unsigned int kDictHashBits = 16; constexpr size_t kDictScratchSize = (1 << kDictHashBits) * sizeof(uint32_t); @@ -305,21 +285,24 @@ inline size_t __device__ __host__ GetMaxCompressedBfrSize(size_t uncomp_size, return uncomp_size + (uncomp_size >> 7) + num_pages * 8; } +struct EncPage; + /** * @brief Struct describing an encoder column chunk */ struct EncColumnChunk { - const parquet_column_device_view *col_desc; //!< Column description + parquet_column_device_view const *col_desc; //!< Column description PageFragment *fragments; //!< First fragment in chunk uint8_t *uncompressed_bfr; //!< Uncompressed page data uint8_t *compressed_bfr; //!< Compressed page data - const statistics_chunk *stats; //!< Fragment statistics + statistics_chunk const *stats; //!< Fragment statistics uint32_t bfr_size; //!< Uncompressed buffer size uint32_t compressed_size; //!< Compressed buffer size uint32_t start_row; //!< First row of chunk uint32_t num_rows; //!< Number of rows in chunk uint32_t num_values; //!< Number of values in chunk. Different from num_rows for nested types uint32_t first_fragment; //!< First fragment of chunk + EncPage *pages; //!< Ptr to pages that belong to this chunk uint32_t first_page; //!< First page of chunk uint32_t num_pages; //!< Number of pages in chunk uint32_t dictionary_id; //!< Dictionary id for this chunk @@ -331,6 +314,28 @@ struct EncColumnChunk { uint32_t ck_stat_size; //!< Size of chunk-level statistics (included in 1st page header) }; +/** + * @brief Struct describing an encoder data page + */ +struct EncPage { + uint8_t *page_data; //!< Ptr to uncompressed page + uint8_t *compressed_data; //!< Ptr to compressed page + uint16_t num_fragments; //!< Number of fragments in page + PageType page_type; //!< Page type + uint8_t dict_bits_plus1; //!< 0=plain, nonzero:bits to encoding dictionary indices + 1 + EncColumnChunk *chunk; //!< Chunk that this page belongs to + uint32_t chunk_id; //!< Index in chunk array + uint32_t hdr_size; //!< Size of page header + uint32_t max_hdr_size; //!< Maximum size of page header + uint32_t max_data_size; //!< Maximum size of coded page data (excluding header) + uint32_t start_row; //!< First row of page + uint32_t num_rows; //!< Rows in page + uint32_t num_leaf_values; //!< Values in page. Different from num_rows in case of nested types + uint32_t num_values; //!< Number of def/rep level values in page. Includes null/empty elements in + //!< non-leaf levels + gpu_inflate_status_s *comp_stat; //!< Ptr to compression status +}; + /** * @brief Launches kernel for parsing the page headers in the column chunks * @@ -446,10 +451,8 @@ dremel_data get_dremel_data(column_view h_col, * @param[in] num_rows Number of rows per column * @param[in] stream CUDA stream to use, default 0 */ -void InitPageFragments(PageFragment *frag, - const parquet_column_device_view *col_desc, - int32_t num_fragments, - int32_t num_columns, +void InitPageFragments(cudf::detail::device_2dspan frag, + device_span col_desc, uint32_t fragment_size, uint32_t num_rows, rmm::cuda_stream_view stream); @@ -460,17 +463,11 @@ void InitPageFragments(PageFragment *frag, * @param[out] groups Statistics groups [num_columns x num_fragments] * @param[in] fragments Page fragments [num_columns x num_fragments] * @param[in] col_desc Column description [num_columns] - * @param[in] num_fragments Number of fragments - * @param[in] num_columns Number of columns - * @param[in] fragment_size Max size of each fragment in rows * @param[in] stream CUDA stream to use, default 0 */ -void InitFragmentStatistics(statistics_group *groups, - const PageFragment *fragments, - const parquet_column_device_view *col_desc, - int32_t num_fragments, - int32_t num_columns, - uint32_t fragment_size, +void InitFragmentStatistics(cudf::detail::device_2dspan groups, + cudf::detail::device_2dspan fragments, + device_span col_desc, rmm::cuda_stream_view stream); /** @@ -485,10 +482,9 @@ void InitFragmentStatistics(statistics_group *groups, * @param[in] chunk_grstats Setup for chunk-level stats * @param[in] stream CUDA stream to use, default 0 */ -void InitEncoderPages(EncColumnChunk *chunks, - EncPage *pages, - const parquet_column_device_view *col_desc, - int32_t num_rowgroups, +void InitEncoderPages(cudf::detail::device_2dspan chunks, + device_span pages, + device_span col_desc, int32_t num_columns, statistics_merge_group *page_grstats = nullptr, statistics_merge_group *chunk_grstats = nullptr, @@ -498,71 +494,48 @@ void InitEncoderPages(EncColumnChunk *chunks, * @brief Launches kernel for packing column data into parquet pages * * @param[in,out] pages Device array of EncPages (unordered) - * @param[in] chunks Column chunks - * @param[in] num_pages Number of pages - * @param[in] start_page First page to encode in page array * @param[out] comp_in Optionally initializes compressor input params * @param[out] comp_out Optionally initializes compressor output params * @param[in] stream CUDA stream to use, default 0 */ -void EncodePages(EncPage *pages, - const EncColumnChunk *chunks, - uint32_t num_pages, - uint32_t start_page = 0, - gpu_inflate_input_s *comp_in = nullptr, - gpu_inflate_status_s *comp_out = nullptr, - rmm::cuda_stream_view stream = rmm::cuda_stream_default); +void EncodePages(device_span pages, + device_span comp_in = {}, + device_span comp_out = {}, + rmm::cuda_stream_view stream = rmm::cuda_stream_default); /** * @brief Launches kernel to make the compressed vs uncompressed chunk-level decision * * @param[in,out] chunks Column chunks (updated with actual compressed/uncompressed sizes) - * @param[in] pages Device array of EncPages - * @param[in] num_chunks Number of column chunks - * @param[in] start_page First page to encode in page array - * @param[in] comp_out Compressor status or nullptr if no compression * @param[in] stream CUDA stream to use, default 0 */ -void DecideCompression(EncColumnChunk *chunks, - const EncPage *pages, - uint32_t num_chunks, - uint32_t start_page, - const gpu_inflate_status_s *comp_out = nullptr, - rmm::cuda_stream_view stream = rmm::cuda_stream_default); +void DecideCompression(device_span chunks, + rmm::cuda_stream_view stream = rmm::cuda_stream_default); /** * @brief Launches kernel to encode page headers * * @param[in,out] pages Device array of EncPages - * @param[in,out] chunks Column chunks - * @param[in] num_pages Number of pages - * @param[in] start_page First page to encode in page array * @param[in] comp_out Compressor status or nullptr if no compression * @param[in] page_stats Optional page-level statistics to be included in page header * @param[in] chunk_stats Optional chunk-level statistics to be encoded * @param[in] stream CUDA stream to use, default 0 */ -void EncodePageHeaders(EncPage *pages, - EncColumnChunk *chunks, - uint32_t num_pages, - uint32_t start_page = 0, - const gpu_inflate_status_s *comp_out = nullptr, - const statistics_chunk *page_stats = nullptr, - const statistics_chunk *chunk_stats = nullptr, - rmm::cuda_stream_view stream = rmm::cuda_stream_default); +void EncodePageHeaders(device_span pages, + device_span comp_out = {}, + device_span page_stats = {}, + const statistics_chunk *chunk_stats = nullptr, + rmm::cuda_stream_view stream = rmm::cuda_stream_default); /** * @brief Launches kernel to gather pages to a single contiguous block per chunk * * @param[in,out] chunks Column chunks * @param[in] pages Device array of EncPages - * @param[in] num_chunks Number of column chunks - * @param[in] comp_out Compressor status * @param[in] stream CUDA stream to use, default 0 */ -void GatherPages(EncColumnChunk *chunks, - const EncPage *pages, - uint32_t num_chunks, +void GatherPages(device_span chunks, + device_span pages, rmm::cuda_stream_view stream); /** @@ -570,14 +543,10 @@ void GatherPages(EncColumnChunk *chunks, * * @param[in] chunks Column chunks * @param[in] dev_scratch Device scratch data (kDictScratchSize bytes per dictionary) - * @param[in] scratch_size size of scratch data in bytes - * @param[in] num_chunks Number of column chunks * @param[in] stream CUDA stream to use, default 0 */ -void BuildChunkDictionaries(EncColumnChunk *chunks, +void BuildChunkDictionaries(device_span chunks, uint32_t *dev_scratch, - size_t scratch_size, - uint32_t num_chunks, rmm::cuda_stream_view stream); } // namespace gpu diff --git a/cpp/src/io/parquet/writer_impl.cu b/cpp/src/io/parquet/writer_impl.cu index b5700af2d6e..0b5a8a0d501 100644 --- a/cpp/src/io/parquet/writer_impl.cu +++ b/cpp/src/io/parquet/writer_impl.cu @@ -717,88 +717,64 @@ gpu::parquet_column_device_view parquet_column_view::get_device_view(rmm::cuda_s return desc; } -void writer::impl::init_page_fragments(hostdevice_vector &frag, - hostdevice_vector &col_desc, - uint32_t num_columns, - uint32_t num_fragments, +void writer::impl::init_page_fragments(cudf::detail::hostdevice_2dvector &frag, + device_span col_desc, uint32_t num_rows, uint32_t fragment_size) { - gpu::InitPageFragments(frag.device_ptr(), - col_desc.device_ptr(), - num_fragments, - num_columns, - fragment_size, - num_rows, - stream); + gpu::InitPageFragments(frag, col_desc, fragment_size, num_rows, stream); frag.device_to_host(stream, true); } void writer::impl::gather_fragment_statistics( - statistics_chunk *frag_stats_chunk, - hostdevice_vector &frag, - hostdevice_vector &col_desc, - uint32_t num_columns, - uint32_t num_fragments, - uint32_t fragment_size) + device_2dspan frag_stats_chunk, + device_2dspan frag, + device_span col_desc, + uint32_t num_fragments) { + auto num_columns = col_desc.size(); rmm::device_uvector frag_stats_group(num_fragments * num_columns, stream); + auto frag_stats_group_2dview = + device_2dspan(frag_stats_group.data(), num_columns, num_fragments); - gpu::InitFragmentStatistics(frag_stats_group.data(), - frag.device_ptr(), - col_desc.device_ptr(), - num_fragments, - num_columns, - fragment_size, - stream); + gpu::InitFragmentStatistics(frag_stats_group_2dview, frag, col_desc, stream); GatherColumnStatistics( - frag_stats_chunk, frag_stats_group.data(), num_fragments * num_columns, stream); + frag_stats_chunk.data(), frag_stats_group.data(), num_fragments * num_columns, stream); stream.synchronize(); } void writer::impl::build_chunk_dictionaries( - hostdevice_vector &chunks, - hostdevice_vector &col_desc, - uint32_t num_rowgroups, + hostdevice_2dvector &chunks, + device_span col_desc, uint32_t num_columns, uint32_t num_dictionaries) { - size_t dict_scratch_size = (size_t)num_dictionaries * gpu::kDictScratchSize; - auto dict_scratch = cudf::detail::make_zeroed_device_uvector_async( - dict_scratch_size / sizeof(uint32_t), stream); chunks.host_to_device(stream); - gpu::BuildChunkDictionaries(chunks.device_ptr(), - dict_scratch.data(), - dict_scratch_size, - num_rowgroups * num_columns, - stream); - gpu::InitEncoderPages(chunks.device_ptr(), - nullptr, - col_desc.device_ptr(), - num_rowgroups, - num_columns, - nullptr, - nullptr, - stream); + if (num_dictionaries > 0) { + size_t dict_scratch_size = (size_t)num_dictionaries * gpu::kDictScratchSize; + auto dict_scratch = cudf::detail::make_zeroed_device_uvector_async( + dict_scratch_size / sizeof(uint32_t), stream); + + gpu::BuildChunkDictionaries(chunks.device_view().flat_view(), dict_scratch.data(), stream); + } + gpu::InitEncoderPages(chunks, {}, col_desc, num_columns, nullptr, nullptr, stream); chunks.device_to_host(stream, true); } -void writer::impl::init_encoder_pages(hostdevice_vector &chunks, - hostdevice_vector &col_desc, - gpu::EncPage *pages, +void writer::impl::init_encoder_pages(hostdevice_2dvector &chunks, + device_span col_desc, + device_span pages, statistics_chunk *page_stats, statistics_chunk *frag_stats, - uint32_t num_rowgroups, uint32_t num_columns, uint32_t num_pages, uint32_t num_stats_bfr) { rmm::device_uvector page_stats_mrg(num_stats_bfr, stream); chunks.host_to_device(stream); - InitEncoderPages(chunks.device_ptr(), + InitEncoderPages(chunks, pages, - col_desc.device_ptr(), - num_rowgroups, + col_desc, num_columns, (num_stats_bfr) ? page_stats_mrg.data() : nullptr, (num_stats_bfr > num_pages) ? page_stats_mrg.data() + num_pages : nullptr, @@ -816,49 +792,49 @@ void writer::impl::init_encoder_pages(hostdevice_vector &ch stream.synchronize(); } -void writer::impl::encode_pages(hostdevice_vector &chunks, - gpu::EncPage *pages, - uint32_t num_columns, +void writer::impl::encode_pages(hostdevice_2dvector &chunks, + device_span pages, uint32_t pages_in_batch, uint32_t first_page_in_batch, uint32_t rowgroups_in_batch, uint32_t first_rowgroup, - gpu_inflate_input_s *comp_in, - gpu_inflate_status_s *comp_out, const statistics_chunk *page_stats, const statistics_chunk *chunk_stats) { - gpu::EncodePages( - pages, chunks.device_ptr(), pages_in_batch, first_page_in_batch, comp_in, comp_out, stream); + auto batch_pages = pages.subspan(first_page_in_batch, pages_in_batch); + + auto batch_pages_stats = + (page_stats != nullptr) + ? device_span(page_stats + first_page_in_batch, pages_in_batch) + : device_span(); + + uint32_t max_comp_pages = + (compression_ != parquet::Compression::UNCOMPRESSED) ? pages_in_batch : 0; + + rmm::device_uvector compression_input(max_comp_pages, stream); + rmm::device_uvector compression_status(max_comp_pages, stream); + + device_span comp_in{compression_input.data(), compression_input.size()}; + device_span comp_stat{compression_status.data(), compression_status.size()}; + + gpu::EncodePages(batch_pages, comp_in, comp_stat, stream); switch (compression_) { case parquet::Compression::SNAPPY: - CUDA_TRY(gpu_snap(comp_in, comp_out, pages_in_batch, stream)); + CUDA_TRY(gpu_snap(comp_in.data(), comp_stat.data(), pages_in_batch, stream)); break; default: break; } // TBD: Not clear if the official spec actually allows dynamically turning off compression at the // chunk-level - DecideCompression(chunks.device_ptr() + first_rowgroup * num_columns, - pages, - rowgroups_in_batch * num_columns, - first_page_in_batch, - comp_out, - stream); - EncodePageHeaders(pages, - chunks.device_ptr(), - pages_in_batch, - first_page_in_batch, - comp_out, - page_stats, - chunk_stats, - stream); - GatherPages(chunks.device_ptr() + first_rowgroup * num_columns, - pages, - rowgroups_in_batch * num_columns, - stream); - CUDA_TRY(cudaMemcpyAsync(&chunks[first_rowgroup * num_columns], - chunks.device_ptr() + first_rowgroup * num_columns, - rowgroups_in_batch * num_columns * sizeof(gpu::EncColumnChunk), + auto d_chunks_in_batch = chunks.device_view().subspan(first_rowgroup, rowgroups_in_batch); + DecideCompression(d_chunks_in_batch.flat_view(), stream); + EncodePageHeaders(batch_pages, comp_stat, batch_pages_stats, chunk_stats, stream); + GatherPages(d_chunks_in_batch.flat_view(), pages, stream); + + auto h_chunks_in_batch = chunks.host_view().subspan(first_rowgroup, rowgroups_in_batch); + CUDA_TRY(cudaMemcpyAsync(h_chunks_in_batch.data(), + d_chunks_in_batch.data(), + d_chunks_in_batch.flat_view().size_bytes(), cudaMemcpyDeviceToHost, stream.value())); stream.synchronize(); @@ -997,15 +973,16 @@ void writer::impl::write(table_view const &table) "fragment size cannot be greater than max_page_fragment_size"); uint32_t num_fragments = (uint32_t)((num_rows + fragment_size - 1) / fragment_size); - hostdevice_vector fragments(num_columns * num_fragments, stream); + cudf::detail::hostdevice_2dvector fragments( + num_columns, num_fragments, stream); - if (fragments.size() != 0) { + if (num_fragments != 0) { // Move column info to device col_desc.host_to_device(stream); leaf_column_views = create_leaf_column_device_views( col_desc, *parent_column_table_device_view, stream); - init_page_fragments(fragments, col_desc, num_columns, num_fragments, num_rows, fragment_size); + init_page_fragments(fragments, col_desc, num_rows, fragment_size); } size_t global_rowgroup_base = md.row_groups.size(); @@ -1018,7 +995,7 @@ void writer::impl::write(table_view const &table) size_t fragment_data_size = 0; // Replace with STL algorithm to transform and sum for (auto i = 0; i < num_columns; i++) { - fragment_data_size += fragments[i * num_fragments + f].fragment_data_size; + fragment_data_size += fragments[i][f].fragment_data_size; } if (f > rowgroup_start && (rowgroup_size + fragment_data_size > max_rowgroup_size_ || (f + 1 - rowgroup_start) * fragment_size > max_rowgroup_rows_)) { @@ -1043,13 +1020,14 @@ void writer::impl::write(table_view const &table) if (stats_granularity_ != statistics_freq::STATISTICS_NONE) { frag_stats.resize(num_fragments * num_columns, stream); if (frag_stats.size() != 0) { - gather_fragment_statistics( - frag_stats.data(), fragments, col_desc, num_columns, num_fragments, fragment_size); + auto frag_stats_2dview = + device_2dspan(frag_stats.data(), num_columns, num_fragments); + gather_fragment_statistics(frag_stats_2dview, fragments, col_desc, num_fragments); } } // Initialize row groups and column chunks uint32_t num_chunks = num_rowgroups * num_columns; - hostdevice_vector chunks(num_chunks, stream); + hostdevice_2dvector chunks(num_rowgroups, num_columns, stream); uint32_t num_dictionaries = 0; for (uint32_t r = 0, global_r = global_rowgroup_base, f = 0, start_row = 0; r < num_rowgroups; r++, global_r++) { @@ -1058,39 +1036,31 @@ void writer::impl::write(table_view const &table) md.row_groups[global_r].total_byte_size = 0; md.row_groups[global_r].columns.resize(num_columns); for (int i = 0; i < num_columns; i++) { - gpu::EncColumnChunk *ck = &chunks[r * num_columns + i]; + gpu::EncColumnChunk *ck = &chunks[r][i]; bool dict_enable = false; - ck->col_desc = col_desc.device_ptr() + i; - ck->uncompressed_bfr = nullptr; - ck->compressed_bfr = nullptr; - ck->bfr_size = 0; - ck->compressed_size = 0; - ck->fragments = fragments.device_ptr() + i * num_fragments + f; + *ck = {}; + ck->col_desc = col_desc.device_ptr() + i; + ck->fragments = &fragments.device_view()[i][f]; ck->stats = (frag_stats.size() != 0) ? frag_stats.data() + i * num_fragments + f : nullptr; - ck->start_row = start_row; - ck->num_rows = (uint32_t)md.row_groups[global_r].num_rows; - ck->first_fragment = i * num_fragments + f; + ck->start_row = start_row; + ck->num_rows = (uint32_t)md.row_groups[global_r].num_rows; + ck->first_fragment = i * num_fragments + f; + auto chunk_fragments = fragments[i].subspan(f, fragments_in_chunk); ck->num_values = - std::accumulate(fragments.host_ptr(i * num_fragments + f), - fragments.host_ptr(i * num_fragments + f) + fragments_in_chunk, - 0, - [](uint32_t l, auto r) { return l + r.num_values; }); - ck->first_page = 0; - ck->num_pages = 0; - ck->is_compressed = 0; + std::accumulate(chunk_fragments.begin(), chunk_fragments.end(), 0, [](uint32_t l, auto r) { + return l + r.num_values; + }); ck->dictionary_id = num_dictionaries; - ck->ck_stat_size = 0; if (col_desc[i].dict_data) { - const gpu::PageFragment *ck_frag = &fragments[i * num_fragments + f]; - size_t plain_size = 0; - size_t dict_size = 1; - uint32_t num_dict_vals = 0; + size_t plain_size = 0; + size_t dict_size = 1; + uint32_t num_dict_vals = 0; for (uint32_t j = 0; j < fragments_in_chunk && num_dict_vals < 65536; j++) { - plain_size += ck_frag[j].fragment_data_size; - dict_size += - ck_frag[j].dict_data_size + ((num_dict_vals > 256) ? 2 : 1) * ck_frag[j].non_nulls; - num_dict_vals += ck_frag[j].num_dict_vals; + plain_size += chunk_fragments[j].fragment_data_size; + dict_size += chunk_fragments[j].dict_data_size + + ((num_dict_vals > 256) ? 2 : 1) * chunk_fragments[j].non_nulls; + num_dict_vals += chunk_fragments[j].num_dict_vals; } if (dict_size < plain_size) { parquet_columns[i].use_dictionary(true); @@ -1119,7 +1089,7 @@ void writer::impl::write(table_view const &table) // Build chunk dictionaries and count pages if (num_chunks != 0) { - build_chunk_dictionaries(chunks, col_desc, num_rowgroups, num_columns, num_dictionaries); + build_chunk_dictionaries(chunks, col_desc, num_columns, num_dictionaries); } // Initialize batches of rowgroups to encode (mainly to limit peak memory usage) @@ -1134,7 +1104,7 @@ void writer::impl::write(table_view const &table) size_t rowgroup_size = 0; if (r < num_rowgroups) { for (int i = 0; i < num_columns; i++) { - gpu::EncColumnChunk *ck = &chunks[r * num_columns + i]; + gpu::EncColumnChunk *ck = &chunks[r][i]; ck->first_page = num_pages; num_pages += ck->num_pages; pages_in_batch += ck->num_pages; @@ -1164,22 +1134,20 @@ void writer::impl::write(table_view const &table) (compression_ != parquet::Compression::UNCOMPRESSED) ? gpu::GetMaxCompressedBfrSize(max_uncomp_bfr_size, max_pages_in_batch) : 0; - uint32_t max_comp_pages = - (compression_ != parquet::Compression::UNCOMPRESSED) ? max_pages_in_batch : 0; uint32_t num_stats_bfr = (stats_granularity_ != statistics_freq::STATISTICS_NONE) ? num_pages + num_chunks : 0; rmm::device_buffer uncomp_bfr(max_uncomp_bfr_size, stream); rmm::device_buffer comp_bfr(max_comp_bfr_size, stream); - rmm::device_uvector comp_in(max_comp_pages, stream); - rmm::device_uvector comp_out(max_comp_pages, stream); rmm::device_uvector pages(num_pages, stream); + + // This contains stats for both the pages and the rowgroups. TODO: make them separate. rmm::device_uvector page_stats(num_stats_bfr, stream); for (uint32_t b = 0, r = 0; b < (uint32_t)batch_list.size(); b++) { uint8_t *bfr = static_cast(uncomp_bfr.data()); uint8_t *bfr_c = static_cast(comp_bfr.data()); for (uint32_t j = 0; j < batch_list[b]; j++, r++) { for (int i = 0; i < num_columns; i++) { - gpu::EncColumnChunk *ck = &chunks[r * num_columns + i]; + gpu::EncColumnChunk *ck = &chunks[r][i]; ck->uncompressed_bfr = bfr; ck->compressed_bfr = bfr_c; bfr += ck->bfr_size; @@ -1191,10 +1159,9 @@ void writer::impl::write(table_view const &table) if (num_pages != 0) { init_encoder_pages(chunks, col_desc, - pages.data(), + {pages.data(), pages.size()}, (num_stats_bfr) ? page_stats.data() : nullptr, (num_stats_bfr) ? frag_stats.data() : nullptr, - num_rowgroups, num_columns, num_pages, num_stats_bfr); @@ -1207,26 +1174,24 @@ void writer::impl::write(table_view const &table) b++) { // Count pages in this batch uint32_t rnext = r + batch_list[b]; - uint32_t first_page_in_batch = chunks[r * num_columns].first_page; + uint32_t first_page_in_batch = chunks[r][0].first_page; uint32_t first_page_in_next_batch = - (rnext < num_rowgroups) ? chunks[rnext * num_columns].first_page : num_pages; + (rnext < num_rowgroups) ? chunks[rnext][0].first_page : num_pages; uint32_t pages_in_batch = first_page_in_next_batch - first_page_in_batch; + // device_span batch_pages{pages.data() + first_page_in_batch, } encode_pages( chunks, - pages.data(), - num_columns, + {pages.data(), pages.size()}, pages_in_batch, first_page_in_batch, batch_list[b], r, - comp_in.data(), - comp_out.data(), (stats_granularity_ == statistics_freq::STATISTICS_PAGE) ? page_stats.data() : nullptr, (stats_granularity_ != statistics_freq::STATISTICS_NONE) ? page_stats.data() + num_pages : nullptr); for (; r < rnext; r++, global_r++) { for (auto i = 0; i < num_columns; i++) { - gpu::EncColumnChunk *ck = &chunks[r * num_columns + i]; + gpu::EncColumnChunk *ck = &chunks[r][i]; uint8_t *dev_bfr; if (ck->is_compressed) { md.row_groups[global_r].columns[i].meta_data.codec = compression_; diff --git a/cpp/src/io/parquet/writer_impl.hpp b/cpp/src/io/parquet/writer_impl.hpp index e5103122033..34c8347e79e 100644 --- a/cpp/src/io/parquet/writer_impl.hpp +++ b/cpp/src/io/parquet/writer_impl.hpp @@ -48,6 +48,9 @@ struct parquet_column_view; using namespace cudf::io::parquet; using namespace cudf::io; +using cudf::detail::device_2dspan; +using cudf::detail::host_2dspan; +using cudf::detail::hostdevice_2dvector; /** * @brief Implementation for parquet writer @@ -124,15 +127,11 @@ class writer::impl { * * @param frag Destination page fragments * @param col_desc column description array - * @param num_columns Total number of columns - * @param num_fragments Total number of fragments per column * @param num_rows Total number of rows * @param fragment_size Number of rows per fragment */ - void init_page_fragments(hostdevice_vector& frag, - hostdevice_vector& col_desc, - uint32_t num_columns, - uint32_t num_fragments, + void init_page_fragments(hostdevice_2dvector& frag, + device_span col_desc, uint32_t num_rows, uint32_t fragment_size); @@ -142,28 +141,22 @@ class writer::impl { * @param dst_stats output statistics * @param frag Input page fragments * @param col_desc column description array - * @param num_columns Total number of columns * @param num_fragments Total number of fragments per column - * @param fragment_size Number of rows per fragment */ - void gather_fragment_statistics(statistics_chunk* dst_stats, - hostdevice_vector& frag, - hostdevice_vector& col_desc, - uint32_t num_columns, - uint32_t num_fragments, - uint32_t fragment_size); + void gather_fragment_statistics(device_2dspan dst_stats, + device_2dspan frag, + device_span col_desc, + uint32_t num_fragments); /** * @brief Build per-chunk dictionaries and count data pages * * @param chunks column chunk array * @param col_desc column description array - * @param num_rowgroups Total number of rowgroups * @param num_columns Total number of columns * @param num_dictionaries Total number of dictionaries */ - void build_chunk_dictionaries(hostdevice_vector& chunks, - hostdevice_vector& col_desc, - uint32_t num_rowgroups, + void build_chunk_dictionaries(hostdevice_2dvector& chunks, + device_span col_desc, uint32_t num_columns, uint32_t num_dictionaries); /** @@ -172,17 +165,15 @@ class writer::impl { * @param chunks column chunk array * @param col_desc column description array * @param pages encoder pages array - * @param num_rowgroups Total number of rowgroups * @param num_columns Total number of columns * @param num_pages Total number of pages * @param num_stats_bfr Number of statistics buffers */ - void init_encoder_pages(hostdevice_vector& chunks, - hostdevice_vector& col_desc, - gpu::EncPage* pages, + void init_encoder_pages(hostdevice_2dvector& chunks, + device_span col_desc, + device_span pages, statistics_chunk* page_stats, statistics_chunk* frag_stats, - uint32_t num_rowgroups, uint32_t num_columns, uint32_t num_pages, uint32_t num_stats_bfr); @@ -191,25 +182,19 @@ class writer::impl { * * @param chunks column chunk array * @param pages encoder pages array - * @param num_columns Total number of columns * @param pages_in_batch number of pages in this batch * @param first_page_in_batch first page in batch * @param rowgroups_in_batch number of rowgroups in this batch * @param first_rowgroup first rowgroup in batch - * @param comp_in compressor input array - * @param comp_out compressor status array * @param page_stats optional page-level statistics (nullptr if none) * @param chunk_stats optional chunk-level statistics (nullptr if none) */ - void encode_pages(hostdevice_vector& chunks, - gpu::EncPage* pages, - uint32_t num_columns, + void encode_pages(hostdevice_2dvector& chunks, + device_span pages, uint32_t pages_in_batch, uint32_t first_page_in_batch, uint32_t rowgroups_in_batch, uint32_t first_rowgroup, - gpu_inflate_input_s* comp_in, - gpu_inflate_status_s* comp_out, const statistics_chunk* page_stats, const statistics_chunk* chunk_stats); diff --git a/cpp/src/io/utilities/hostdevice_vector.hpp b/cpp/src/io/utilities/hostdevice_vector.hpp index 62b87f727c4..ee4b23bf831 100644 --- a/cpp/src/io/utilities/hostdevice_vector.hpp +++ b/cpp/src/io/utilities/hostdevice_vector.hpp @@ -154,9 +154,15 @@ class hostdevice_2dvector { operator device_2dspan() { return {_data.device_ptr(), _size}; } operator device_2dspan() const { return {_data.device_ptr(), _size}; } + device_2dspan device_view() { return static_cast>(*this); } + device_2dspan device_view() const { return static_cast>(*this); } + operator host_2dspan() { return {_data.host_ptr(), _size}; } operator host_2dspan() const { return {_data.host_ptr(), _size}; } + host_2dspan host_view() { return static_cast>(*this); } + host_2dspan host_view() const { return static_cast>(*this); } + host_span operator[](size_t row) { return {_data.host_ptr() + host_2dspan::flatten_index(row, 0, _size), _size.second}; From 7623f3978b17be3782c508441fafc45d8d9e258e Mon Sep 17 00:00:00 2001 From: MithunR Date: Mon, 3 May 2021 04:37:26 -0700 Subject: [PATCH 17/65] Update Developer Guide to mention structured bindings (#8116) The developer guide mentions the use of `std::tie()` disaggregate multiple return values from a function that returns `std::tuple` or `std::pair`. This commit adds mention of [Structured Binding](https://en.cppreference.com/w/cpp/language/structured_binding) for the same ends. It also mentions how to work around compiler restrictions when using aliases from structured bindings in lambda captures. Authors: - MithunR (https://github.com/mythrocks) Approvers: - David Wendt (https://github.com/davidwendt) - Mike Wilson (https://github.com/hyperbolic2346) - Nghia Truong (https://github.com/ttnghia) - Mark Harris (https://github.com/harrism) URL: https://github.com/rapidsai/cudf/pull/8116 --- cpp/docs/DEVELOPER_GUIDE.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cpp/docs/DEVELOPER_GUIDE.md b/cpp/docs/DEVELOPER_GUIDE.md index fa59162c345..3169a0884c6 100644 --- a/cpp/docs/DEVELOPER_GUIDE.md +++ b/cpp/docs/DEVELOPER_GUIDE.md @@ -530,6 +530,30 @@ Note: `std::tuple` _could_ be used if not for the fact that Cython does not s only two objects of different types. Multiple objects of the same type may be returned via a `std::vector`. +Alternatively, with C++17 (supported from cudf v0.20), [structured binding](https://en.cppreference.com/w/cpp/language/structured_binding) +may be used to disaggregate multiple return values: + +```c++ +auto [out0, out1] = cudf::return_two_outputs(); +``` + +Note that the compiler might not support capturing aliases defined in a structured binding +in a lambda. One may work around this by using a capture with an initializer instead: + +```c++ +auto [out0, out1] = cudf::return_two_outputs(); + +// Direct capture of alias from structured binding might fail with: +// "error: structured binding cannot be captured" +// auto foo = [out0]() {...}; + +// Use an initializing capture: +auto foo = [&out0 = out0] { + // Use out0 to compute something. + // ... +}; +``` + ## Iterator-based interfaces Increasingly, libcudf is moving toward internal (`detail`) APIs with iterator parameters rather From c7eccc10d02fad9d556cc2faae2c9e629bfa24ad Mon Sep 17 00:00:00 2001 From: "Richard (Rick) Zamora" Date: Mon, 3 May 2021 10:54:20 -0500 Subject: [PATCH 18/65] Fix fragile logic in dask_cudf chunksize parquet test (#8108) This PR loosens some fragile assumptions in dask_cudf's `test_parquet.py::test_chunksize`. **More context**: This change is in preparation for [dask#7557](https://github.com/dask/dask/pull/7557), which will change the behavior of the `chunksize` argument in `dask.dataframe.read_parquet`. Further changes will be required in dask_cudf to enable (optimal) multi-file partition aggregation, but this PR should avoid any temporary CI failures. Authors: - Richard (Rick) Zamora (https://github.com/rjzamora) Approvers: - Keith Kraus (https://github.com/kkraus14) URL: https://github.com/rapidsai/cudf/pull/8108 --- python/dask_cudf/dask_cudf/io/tests/test_parquet.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/python/dask_cudf/dask_cudf/io/tests/test_parquet.py b/python/dask_cudf/dask_cudf/io/tests/test_parquet.py index f8d877048f4..d80583c6959 100644 --- a/python/dask_cudf/dask_cudf/io/tests/test_parquet.py +++ b/python/dask_cudf/dask_cudf/io/tests/test_parquet.py @@ -9,7 +9,7 @@ import dask from dask import dataframe as dd -from dask.utils import natural_sort_key, parse_bytes +from dask.utils import natural_sort_key import cudf @@ -294,7 +294,6 @@ def test_chunksize(tmpdir, chunksize, metadata): nparts = 2 df_size = 100 row_group_size = 5 - row_group_byte_size = 451 # Empirically measured df = pd.DataFrame( { @@ -334,12 +333,7 @@ def test_chunksize(tmpdir, chunksize, metadata): if not chunksize: assert ddf2.npartitions == num_row_groups else: - # Check that we are really aggregating - df_byte_size = row_group_byte_size * num_row_groups - expected = df_byte_size // parse_bytes(chunksize) - remainder = (df_byte_size % parse_bytes(chunksize)) > 0 - expected += int(remainder) * nparts - assert ddf2.npartitions == max(nparts, expected) + assert ddf2.npartitions < num_row_groups @pytest.mark.parametrize("row_groups", [1, 3, 10, 12]) From 27ae8c13f6b6f0de14b2846500bd8cc3f66a29bf Mon Sep 17 00:00:00 2001 From: Charles Blackmon-Luca <20627856+charlesbluca@users.noreply.github.com> Date: Mon, 3 May 2021 12:02:09 -0400 Subject: [PATCH 19/65] Redirect callable aggregations to their named equivalent in dask-cuDF (#8048) Redirects Python built-in functions to their named equivalent aggregation in Dask-cuDF; this ensures that the Dask-cuDF codepath is used for these aggregations regardless of how they're specified, see [#7812 (comment)](https://github.com/rapidsai/cudf/issues/7812#issuecomment-820518107). Authors: - Charles Blackmon-Luca (https://github.com/charlesbluca) Approvers: - Keith Kraus (https://github.com/kkraus14) URL: https://github.com/rapidsai/cudf/pull/8048 --- python/dask_cudf/dask_cudf/groupby.py | 23 +++++++++++++++++-- .../dask_cudf/dask_cudf/tests/test_groupby.py | 21 +++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/python/dask_cudf/dask_cudf/groupby.py b/python/dask_cudf/dask_cudf/groupby.py index 61cebbfd8db..73fe1bd2196 100644 --- a/python/dask_cudf/dask_cudf/groupby.py +++ b/python/dask_cudf/dask_cudf/groupby.py @@ -60,6 +60,7 @@ def mean(self, split_every=None, split_out=1): def aggregate(self, arg, split_every=None, split_out=1): if arg == "size": return self.size() + arg = _redirect_aggs(arg) _supported = {"count", "mean", "std", "var", "sum", "min", "max"} if ( @@ -106,6 +107,7 @@ def mean(self, split_every=None, split_out=1): def aggregate(self, arg, split_every=None, split_out=1): if arg == "size": return self.size() + arg = _redirect_aggs(arg) _supported = {"count", "mean", "std", "var", "sum", "min", "max"} if ( @@ -159,7 +161,7 @@ def groupby_agg( split_out = split_out or 1 # Standardize `gb_cols` and `columns` lists - aggs = aggs_in.copy() + aggs = _redirect_aggs(aggs_in.copy()) if isinstance(gb_cols, str): gb_cols = [gb_cols] columns = [c for c in ddf.columns if c not in gb_cols] @@ -277,8 +279,25 @@ def groupby_agg( return new_dd_object(graph, gb_agg_name, _meta, divisions) +def _redirect_aggs(arg): + """ Redirect aggregations to their corresponding name in cuDF + """ + redirects = {sum: "sum", max: "max", min: "min"} + if isinstance(arg, dict): + new_arg = dict() + for col in arg: + if isinstance(arg[col], list): + new_arg[col] = [redirects.get(agg, agg) for agg in arg[col]] + else: + new_arg[col] = redirects.get(arg[col], arg[col]) + return new_arg + if isinstance(arg, list): + return [redirects.get(agg, agg) for agg in arg] + return redirects.get(arg, arg) + + def _is_supported(arg, supported: set): - """ Check that aggregations in `args` is a subset of `supportd` + """ Check that aggregations in `arg` are a subset of `supported` """ if isinstance(arg, (list, dict)): if isinstance(arg, dict): diff --git a/python/dask_cudf/dask_cudf/tests/test_groupby.py b/python/dask_cudf/dask_cudf/tests/test_groupby.py index db469b3b2a9..e3a3045dcc7 100644 --- a/python/dask_cudf/dask_cudf/tests/test_groupby.py +++ b/python/dask_cudf/dask_cudf/tests/test_groupby.py @@ -536,6 +536,27 @@ def test_groupby_agg_params(npartitions, split_every, split_out, as_index): dd.assert_eq(gf, pf) +@pytest.mark.parametrize( + "aggregations", [(sum, "sum"), (max, "max"), (min, "min")] +) +def test_groupby_agg_redirect(aggregations): + pdf = pd.DataFrame( + { + "x": np.random.randint(0, 5, size=10000), + "y": np.random.normal(size=10000), + } + ) + + gdf = cudf.DataFrame.from_pandas(pdf) + + ddf = dask_cudf.from_cudf(gdf, npartitions=5) + + a = ddf.groupby("x").agg({"x": aggregations[0]}).compute() + b = ddf.groupby("x").agg({"x": aggregations[1]}).compute() + + dd.assert_eq(a, b) + + @pytest.mark.parametrize( "arg", [["not_supported"], {"a": "not_supported"}, {"a": ["not_supported"]}], From 6ab91f2c9ca25ba6c0f5e7e65a599c41548970ac Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 3 May 2021 12:17:21 -0600 Subject: [PATCH 20/65] Implement interleave_columns for list type (#8046) This PR implements `lists::interleave_columns` that returns a single column by interleaving rows of the given table of list elements. For example: ``` s1 = [{0, 1}, {2, 3, 4}, {5}, {}, {6, 7}] s2 = [{8}, {9}, {}, {10, 11, 12}, {13, 14, 15, 16}] r = lists::interleave_columns( table_view{s1, s2} ) r is now [{0, 1}, {8}, {2, 3, 4}, {9}, {5}, {}, {}, {10, 11, 12}, {6, 7}, {13, 14, 15, 16}] ``` Currently, only lists columns of one depth level are supported. Authors: - Nghia Truong (https://github.com/ttnghia) Approvers: - https://github.com/nvdbaranec - Jordan Jacobelli (https://github.com/Ethyling) - MithunR (https://github.com/mythrocks) - Jake Hemstad (https://github.com/jrhemstad) URL: https://github.com/rapidsai/cudf/pull/8046 --- conda/recipes/libcudf/meta.yaml | 1 + cpp/CMakeLists.txt | 3 +- .../cudf/lists/detail/interleave_columns.hpp | 55 +++ cpp/src/lists/interleave_columns.cu | 359 ++++++++++++++++++ cpp/src/reshape/interleave_columns.cu | 17 +- .../reshape/interleave_columns_tests.cpp | 309 ++++++++++++++- 6 files changed, 734 insertions(+), 10 deletions(-) create mode 100644 cpp/include/cudf/lists/detail/interleave_columns.hpp create mode 100644 cpp/src/lists/interleave_columns.cu diff --git a/conda/recipes/libcudf/meta.yaml b/conda/recipes/libcudf/meta.yaml index 4386a62e32a..ebdf6a3fb12 100644 --- a/conda/recipes/libcudf/meta.yaml +++ b/conda/recipes/libcudf/meta.yaml @@ -135,6 +135,7 @@ test: - test -f $PREFIX/include/cudf/lists/detail/concatenate.hpp - test -f $PREFIX/include/cudf/lists/detail/copying.hpp - test -f $PREFIX/include/cudf/lists/detail/drop_list_duplicates.hpp + - test -f $PREFIX/include/cudf/lists/detail/interleave_columns.hpp - test -f $PREFIX/include/cudf/lists/detail/sorting.hpp - test -f $PREFIX/include/cudf/lists/count_elements.hpp - test -f $PREFIX/include/cudf/lists/explode.hpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 82c80b89e1f..1689de29b05 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -268,9 +268,10 @@ add_library(cudf src/lists/copying/gather.cu src/lists/copying/segmented_gather.cu src/lists/count_elements.cu + src/lists/drop_list_duplicates.cu src/lists/explode.cu src/lists/extract.cu - src/lists/drop_list_duplicates.cu + src/lists/interleave_columns.cu src/lists/lists_column_factories.cu src/lists/lists_column_view.cu src/lists/segmented_sort.cu diff --git a/cpp/include/cudf/lists/detail/interleave_columns.hpp b/cpp/include/cudf/lists/detail/interleave_columns.hpp new file mode 100644 index 00000000000..7ae90779fdc --- /dev/null +++ b/cpp/include/cudf/lists/detail/interleave_columns.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#include + +namespace cudf { +namespace lists { +namespace detail { + +/** + * @brief Returns a single column by interleaving rows of the given table of list elements. + * + * @code{.pseudo} + * s1 = [{0, 1}, {2, 3, 4}, {5}, {}, {6, 7}] + * s2 = [{8}, {9}, {}, {10, 11, 12}, {13, 14, 15, 16}] + * r = lists::interleave_columns(s1, s2) + * r is now [{0, 1}, {8}, {2, 3, 4}, {9}, {5}, {}, {}, {10, 11, 12}, {6, 7}, {13, 14, 15, 16}] + * @endcode + * + * @throws cudf::logic_error if any column of the input table is not a lists columns. + * @throws cudf::logic_error if any lists column contains nested typed entry. + * @throws cudf::logic_error if all lists columns do not have the same entry type. + * + * @param input Table containing lists columns to interleave. + * @param has_null_mask A boolean flag indicating that the input columns have a null mask. + * @param stream CUDA stream used for device memory operations and kernel launches. + * @param mr Device memory resource used to allocate the returned column's device memory. + * @return The interleaved columns as a single column. + */ +std::unique_ptr interleave_columns( + table_view const& input, + bool has_null_mask, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + +} // namespace detail +} // namespace lists +} // namespace cudf diff --git a/cpp/src/lists/interleave_columns.cu b/cpp/src/lists/interleave_columns.cu new file mode 100644 index 00000000000..eb205ae8076 --- /dev/null +++ b/cpp/src/lists/interleave_columns.cu @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace cudf { +namespace lists { +namespace detail { +namespace { +/** + * @brief Generate list offsets and list validities for the output lists column from the table_view + * of the input lists columns. + */ +std::pair, rmm::device_uvector> +generate_list_offsets_and_validities(table_view const& input, + bool has_null_mask, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) +{ + auto const num_cols = input.num_columns(); + auto const num_rows = input.num_rows(); + auto const num_output_lists = num_rows * num_cols; + auto const table_dv_ptr = table_device_view::create(input); + + // The output offsets column. + static_assert(sizeof(offset_type) == sizeof(int32_t)); + static_assert(sizeof(size_type) == sizeof(int32_t)); + auto list_offsets = make_numeric_column( + data_type{type_id::INT32}, num_output_lists + 1, mask_state::UNALLOCATED, stream, mr); + auto const d_offsets = list_offsets->mutable_view().template begin(); + + // The array of int8_t to store validities for list elements. + auto validities = rmm::device_uvector(has_null_mask ? num_output_lists : 0, stream); + + // Compute list sizes and validities. + thrust::transform( + rmm::exec_policy(stream), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_output_lists), + d_offsets, + [num_cols, + table_dv = *table_dv_ptr, + d_validities = validities.begin(), + has_null_mask] __device__(size_type const idx) { + auto const col_id = idx % num_cols; + auto const list_id = idx / num_cols; + auto const& lists_col = table_dv.column(col_id); + if (has_null_mask) { d_validities[idx] = static_cast(lists_col.is_valid(list_id)); } + auto const list_offsets = + lists_col.child(lists_column_view::offsets_column_index).template data() + + lists_col.offset(); + return list_offsets[list_id + 1] - list_offsets[list_id]; + }); + + // Compute offsets from sizes. + thrust::exclusive_scan( + rmm::exec_policy(stream), d_offsets, d_offsets + num_output_lists + 1, d_offsets); + + return {std::move(list_offsets), std::move(validities)}; +} + +/** + * @brief Compute string sizes, string validities, and interleave string lists functor. + * + * This functor is executed twice. In the first pass, the sizes and validities of the output strings + * will be computed. In the second pass, this will interleave the lists of strings of the given + * table containing those lists. + */ +struct compute_string_sizes_and_interleave_lists_fn { + table_device_view const table_dv; + + // Store list offsets of the output lists column. + offset_type const* const dst_list_offsets; + + // Flag to specify whether to compute string validities. + bool const has_null_mask; + + // Store offsets of the strings. + offset_type* d_offsets{nullptr}; + + // If d_chars == nullptr: only compute sizes and validities of the output strings. + // If d_chars != nullptr: only concatenate strings. + char* d_chars{nullptr}; + + // We need to set `1` or `0` for the validities of the strings in the child column. + int8_t* d_validities{nullptr}; + + __device__ void operator()(size_type const idx) + { + auto const num_cols = table_dv.num_columns(); + auto const col_id = idx % num_cols; + auto const list_id = idx / num_cols; + + auto const& lists_col = table_dv.column(col_id); + if (has_null_mask and lists_col.is_null(list_id)) { return; } + + auto const list_offsets = + lists_col.child(lists_column_view::offsets_column_index).template data() + + lists_col.offset(); + auto const& str_col = lists_col.child(lists_column_view::child_column_index); + auto const str_offsets = + str_col.child(strings_column_view::offsets_column_index).template data(); + + // The indices of the strings within the source list. + auto const start_str_idx = list_offsets[list_id]; + auto const end_str_idx = list_offsets[list_id + 1]; + + // read_idx and write_idx are indices of string elements. + size_type write_idx = dst_list_offsets[idx]; + + if (not d_chars) { // just compute sizes and validities of strings within a list + for (auto read_idx = start_str_idx; read_idx < end_str_idx; ++read_idx, ++write_idx) { + if (has_null_mask) { + d_validities[write_idx] = static_cast(str_col.is_valid(read_idx)); + } + d_offsets[write_idx] = str_offsets[read_idx + 1] - str_offsets[read_idx]; + } + } else { // just copy the entire memory region containing all strings in the list + // start_byte and end_byte are indices of character of the string elements. + auto const start_byte = str_offsets[start_str_idx]; + auto const end_byte = str_offsets[end_str_idx]; + if (start_byte < end_byte) { + auto const input_ptr = + str_col.child(strings_column_view::chars_column_index).template data() + start_byte; + auto const output_ptr = d_chars + d_offsets[write_idx]; + thrust::copy(thrust::seq, input_ptr, input_ptr + end_byte - start_byte, output_ptr); + } + } + } +}; + +/** + * @brief Struct used in type_dispatcher to interleave list entries of the input lists columns and + * output the results into a destination column. + */ +struct interleave_list_entries_fn { + template + std::enable_if_t, std::unique_ptr> operator()( + table_view const& input, + column_view const& output_list_offsets, + size_type num_output_lists, + size_type num_output_entries, + bool has_null_mask, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) const noexcept + { + auto const table_dv_ptr = table_device_view::create(input); + auto const comp_fn = compute_string_sizes_and_interleave_lists_fn{ + *table_dv_ptr, output_list_offsets.template begin(), has_null_mask}; + + if (has_null_mask) { + auto [offsets_column, chars_column, null_mask, null_count] = + cudf::strings::detail::make_strings_children_with_null_mask( + comp_fn, num_output_lists, num_output_entries, stream, mr); + return make_strings_column(num_output_entries, + std::move(offsets_column), + std::move(chars_column), + null_count, + std::move(null_mask), + stream, + mr); + } + + auto [offsets_column, chars_column] = cudf::strings::detail::make_strings_children( + comp_fn, num_output_lists, num_output_entries, stream, mr); + return make_strings_column(num_output_entries, + std::move(offsets_column), + std::move(chars_column), + 0, + rmm::device_buffer{}, + stream, + mr); + } + + template + std::enable_if_t(), std::unique_ptr> operator()( + table_view const& input, + column_view const& output_list_offsets, + size_type num_output_lists, + size_type num_output_entries, + bool has_null_mask, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) const noexcept + { + auto const num_cols = input.num_columns(); + auto const num_rows = input.num_rows(); + auto const table_dv_ptr = table_device_view::create(input); + + // The output child column. + auto output = allocate_like(lists_column_view(*input.begin()).child(), + num_output_entries, + mask_allocation_policy::NEVER, + stream, + mr); + auto output_dv_ptr = mutable_column_device_view::create(*output); + + // The array of int8_t to store entry validities. + auto validities = rmm::device_uvector(has_null_mask ? num_output_entries : 0, stream); + + thrust::for_each_n( + rmm::exec_policy(stream), + thrust::make_counting_iterator(0), + num_output_lists, + [num_cols, + table_dv = *table_dv_ptr, + d_validities = validities.begin(), + d_offsets = output_list_offsets.template begin(), + d_output = output_dv_ptr->template begin(), + has_null_mask] __device__(size_type const idx) { + auto const col_id = idx % num_cols; + auto const list_id = idx / num_cols; + auto const& lists_col = table_dv.column(col_id); + auto const list_offsets = + lists_col.child(lists_column_view::offsets_column_index).template data() + + lists_col.offset(); + auto const& data_col = lists_col.child(lists_column_view::child_column_index); + + // The indices of the entries within the source list. + auto const start_idx = list_offsets[list_id]; + auto const end_idx = list_offsets[list_id + 1]; + auto const write_start = d_offsets[idx]; + + // Fill the validities array if necessary. + if (has_null_mask) { + for (auto read_idx = start_idx, write_idx = write_start; read_idx < end_idx; + ++read_idx, ++write_idx) { + d_validities[write_idx] = static_cast(data_col.is_valid(read_idx)); + } + } + + // Do a copy for the entire list entries. + auto const input_ptr = + reinterpret_cast(data_col.template data() + start_idx); + auto const output_ptr = reinterpret_cast(&d_output[write_start]); + thrust::copy( + thrust::seq, input_ptr, input_ptr + sizeof(T) * (end_idx - start_idx), output_ptr); + }); + + if (has_null_mask) { + auto [null_mask, null_count] = cudf::detail::valid_if( + validities.begin(), validities.end(), thrust::identity{}, stream, mr); + if (null_count > 0) { output->set_null_mask(null_mask, null_count); } + } + + return output; + } + + template + std::enable_if_t and not cudf::is_fixed_width(), + std::unique_ptr> + operator()(table_view const&, + column_view const&, + size_type, + size_type, + bool, + rmm::cuda_stream_view, + rmm::mr::device_memory_resource*) const + { + // Currently, only support string_view and fixed-width types + CUDF_FAIL("Called `interleave_list_entries_fn()` on non-supported types."); + } +}; + +} // anonymous namespace + +/** + * @copydoc cudf::lists::detail::interleave_columns + * + */ +std::unique_ptr interleave_columns(table_view const& input, + bool has_null_mask, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) +{ + auto const entry_type = lists_column_view(*input.begin()).child().type(); + for (auto const& col : input) { + CUDF_EXPECTS(col.type().id() == type_id::LIST, + "All columns of the input table must be of lists column type."); + + auto const child_col = lists_column_view(col).child(); + CUDF_EXPECTS(not cudf::is_nested(child_col.type()), "Nested types are not supported."); + CUDF_EXPECTS(entry_type == child_col.type(), + "The types of entries in the input columns must be the same."); + } + + if (input.num_rows() == 0) { return cudf::empty_like(input.column(0)); } + if (input.num_columns() == 1) { return std::make_unique(*(input.begin()), stream, mr); } + + // Generate offsets of the output lists column. + auto [list_offsets, list_validities] = + generate_list_offsets_and_validities(input, has_null_mask, stream, mr); + auto const offsets_view = list_offsets->view(); + + // Copy entries from the input lists columns to the output lists column - this needed to be + // specialized for different types. + auto const num_output_lists = input.num_rows() * input.num_columns(); + auto const num_output_entries = + cudf::detail::get_value(offsets_view, num_output_lists, stream); + auto list_entries = type_dispatcher(entry_type, + interleave_list_entries_fn{}, + input, + offsets_view, + num_output_lists, + num_output_entries, + has_null_mask, + stream, + mr); + + if (not has_null_mask) { + return make_lists_column(num_output_lists, + std::move(list_offsets), + std::move(list_entries), + 0, + rmm::device_buffer{}, + stream, + mr); + } + + auto [null_mask, null_count] = cudf::detail::valid_if( + list_validities.begin(), list_validities.end(), thrust::identity{}, stream, mr); + return make_lists_column(num_output_lists, + std::move(list_offsets), + std::move(list_entries), + null_count, + null_count ? std::move(null_mask) : rmm::device_buffer{}, + stream, + mr); +} + +} // namespace detail +} // namespace lists +} // namespace cudf diff --git a/cpp/src/reshape/interleave_columns.cu b/cpp/src/reshape/interleave_columns.cu index 9cb6713ba24..667937830f6 100644 --- a/cpp/src/reshape/interleave_columns.cu +++ b/cpp/src/reshape/interleave_columns.cu @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -29,11 +30,23 @@ namespace detail { namespace { struct interleave_columns_functor { template - std::enable_if_t() and not std::is_same::value, + std::enable_if_t() and + not std::is_same::value and + not std::is_same::value, std::unique_ptr> operator()(Args&&... args) { - CUDF_FAIL("interleave_columns not supported for dictionary and list types."); + CUDF_FAIL("Called `interleave_columns` on none-supported data type."); + } + + template + std::enable_if_t::value, std::unique_ptr> + operator()(table_view const& lists_columns, + bool create_mask, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + { + return lists::detail::interleave_columns(lists_columns, create_mask, stream, mr); } template diff --git a/cpp/tests/reshape/interleave_columns_tests.cpp b/cpp/tests/reshape/interleave_columns_tests.cpp index 654df7589e6..68cf19bbe9c 100644 --- a/cpp/tests/reshape/interleave_columns_tests.cpp +++ b/cpp/tests/reshape/interleave_columns_tests.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,12 @@ #include #include -#include #include -#include +#include #include -#include #include -#include -#include "cudf/utilities/traits.hpp" - using namespace cudf::test; template @@ -374,4 +369,304 @@ TYPED_TEST(FixedPointTestBothReps, FixedPointInterleave) } } +namespace { +using StrListsCol = cudf::test::lists_column_wrapper; +using IntListsCol = cudf::test::lists_column_wrapper; +using IntCol = cudf::test::fixed_width_column_wrapper; +using TView = cudf::table_view; + +constexpr bool print_all{false}; // For debugging +constexpr int32_t null{0}; + +auto all_nulls() { return cudf::test::iterator_all_nulls(); } + +auto null_at(cudf::size_type idx) { return cudf::test::iterator_with_null_at(idx); } + +auto null_at(std::vector const& indices) +{ + return cudf::test::iterator_with_null_at(cudf::host_span{indices}); +} + +} // namespace + +struct ListsColumnsInterleaveTest : public cudf::test::BaseFixture { +}; + +TEST_F(ListsColumnsInterleaveTest, InvalidInput) +{ + // Input table contains non-list column + { + auto const col1 = IntCol{}.release(); + auto const col2 = IntListsCol{}.release(); + EXPECT_THROW(cudf::interleave_columns(TView{{col1->view(), col2->view()}}), cudf::logic_error); + } + + // Types mismatch + { + auto const col1 = IntListsCol{}.release(); + auto const col2 = StrListsCol{}.release(); + EXPECT_THROW(cudf::interleave_columns(TView{{col1->view(), col2->view()}}), cudf::logic_error); + } + + // Nested types are not supported + { + auto const col = IntListsCol{{IntListsCol{1, 2, 3}, IntListsCol{4, 5, 6}}}.release(); + EXPECT_THROW(cudf::interleave_columns(TView{{col->view(), col->view()}}), cudf::logic_error); + } +} + +template +struct ListsColumnsInterleaveTypedTest : public cudf::test::BaseFixture { +}; + +using TypesForTest = cudf::test::Concat; +TYPED_TEST_CASE(ListsColumnsInterleaveTypedTest, TypesForTest); + +TYPED_TEST(ListsColumnsInterleaveTypedTest, InterleaveEmptyColumns) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col = ListsCol{}.release(); + auto const results = cudf::interleave_columns(TView{{col->view(), col->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*col, *results, print_all); +} + +TYPED_TEST(ListsColumnsInterleaveTypedTest, InterleaveOneColumnNotNull) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col = ListsCol{{1, 2}, {3, 4}, {5, 6}}.release(); + auto const results = cudf::interleave_columns(TView{{col->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*col, *results, print_all); +} + +TYPED_TEST(ListsColumnsInterleaveTypedTest, InterleaveOneColumnWithNulls) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col = ListsCol{{ListsCol{{1, 2, null}, null_at(2)}, + ListsCol{} /*NULL*/, + ListsCol{{null, 3, 4, 4, 4, 4}, null_at(0)}, + ListsCol{5, 6}}, + null_at(1)} + .release(); + auto const results = cudf::interleave_columns(TView{{col->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*col, *results, print_all); +} + +TYPED_TEST(ListsColumnsInterleaveTypedTest, SimpleInputNoNull) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col1 = ListsCol{{1, 2}, {3, 4}, {5, 6}}.release(); + auto const col2 = ListsCol{{7, 8}, {9, 10}, {11, 12}}.release(); + auto const expected = ListsCol{{1, 2}, {7, 8}, {3, 4}, {9, 10}, {5, 6}, {11, 12}}.release(); + auto const results = cudf::interleave_columns(TView{{col1->view(), col2->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + +TEST_F(ListsColumnsInterleaveTest, SimpleInputStringsColumnsNoNull) +{ + auto const col1 = StrListsCol{ + StrListsCol{"Tomato", "Apple"}, + StrListsCol{"Banana", "Kiwi", "Cherry"}, + StrListsCol{ + "Coconut"}}.release(); + auto const col2 = + StrListsCol{StrListsCol{"Orange"}, StrListsCol{"Lemon", "Peach"}, StrListsCol{}}.release(); + auto const expected = StrListsCol{ + StrListsCol{"Tomato", "Apple"}, + StrListsCol{"Orange"}, + StrListsCol{"Banana", "Kiwi", "Cherry"}, + StrListsCol{"Lemon", "Peach"}, + StrListsCol{"Coconut"}, + StrListsCol{}}.release(); + auto const results = cudf::interleave_columns(TView{{col1->view(), col2->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + +TYPED_TEST(ListsColumnsInterleaveTypedTest, SimpleInputWithNulls) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col1 = ListsCol{{ListsCol{{1, null, 3, 4}, null_at(1)}, + ListsCol{{null, 2, 3, 4}, null_at(0)}, + ListsCol{{null, 2, 3, 4}, null_at(0)}, + ListsCol{} /*NULL*/, + ListsCol{{1, 2, null, 4}, null_at(2)}, + ListsCol{{1, 2, 3, null}, null_at(3)}}, + null_at(3)} + .release(); + auto const col2 = ListsCol{{ListsCol{{10, 11, 12, null}, null_at(3)}, + ListsCol{{13, 14, 15, 16, 17, null}, null_at(5)}, + ListsCol{} /*NULL*/, + ListsCol{{null, 18}, null_at(0)}, + ListsCol{{19, 20, null}, null_at(2)}, + ListsCol{{null}, null_at(0)}}, + null_at(2)} + .release(); + auto const col3 = ListsCol{{ListsCol{} /*NULL*/, + ListsCol{{20, null}, null_at(1)}, + ListsCol{{null, 21, null, null}, null_at({0, 2, 3})}, + ListsCol{}, + ListsCol{22, 23, 24, 25}, + ListsCol{{null, null, null, null, null}, all_nulls()}}, + null_at(0)} + .release(); + auto const expected = ListsCol{{ListsCol{{1, null, 3, 4}, null_at(1)}, + ListsCol{{10, 11, 12, null}, null_at(3)}, + ListsCol{} /*NULL*/, + ListsCol{{null, 2, 3, 4}, null_at(0)}, + ListsCol{{13, 14, 15, 16, 17, null}, null_at(5)}, + ListsCol{{20, null}, null_at(1)}, + ListsCol{{null, 2, 3, 4}, null_at(0)}, + ListsCol{} /*NULL*/, + ListsCol{{null, 21, null, null}, null_at({0, 2, 3})}, + ListsCol{} /*NULL*/, + ListsCol{{null, 18}, null_at(0)}, + ListsCol{}, + ListsCol{{1, 2, null, 4}, null_at(2)}, + ListsCol{{19, 20, null}, null_at(2)}, + ListsCol{22, 23, 24, 25}, + ListsCol{{1, 2, 3, null}, null_at(3)}, + ListsCol{{null}, null_at(0)}, + ListsCol{{null, null, null, null, null}, all_nulls()}}, + null_at({2, 7, 9})} + .release(); + auto const results = cudf::interleave_columns(TView{{col1->view(), col2->view(), col3->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + +TEST_F(ListsColumnsInterleaveTest, SimpleInputStringsColumnsWithNulls) +{ + auto const col1 = StrListsCol{ + StrListsCol{{"Tomato", "Bear" /*NULL*/, "Apple"}, null_at(1)}, + StrListsCol{{"Banana", "Pig" /*NULL*/, "Kiwi", "Cherry", "Whale" /*NULL*/}, null_at({1, 4})}, + StrListsCol{ + "Coconut"}}.release(); + auto const col2 = + StrListsCol{ + {StrListsCol{{"Orange", "Dog" /*NULL*/, "Fox" /*NULL*/, "Duck" /*NULL*/}, null_at({1, 2, 3})}, + StrListsCol{"Lemon", "Peach"}, + StrListsCol{{"Deer" /*NULL*/, "Snake" /*NULL*/, "Horse" /*NULL*/}, all_nulls()}}, /*NULL*/ + null_at(2)} + .release(); + + auto const expected = + StrListsCol{ + {StrListsCol{{"Tomato", "" /*NULL*/, "Apple"}, null_at(1)}, + StrListsCol{{"Orange", "" /*NULL*/, "" /*NULL*/, "" /*NULL*/}, null_at({1, 2, 3})}, + StrListsCol{{"Banana", "" /*NULL*/, "Kiwi", "Cherry", "" /*NULL*/}, null_at({1, 4})}, + StrListsCol{"Lemon", "Peach"}, + StrListsCol{"Coconut"}, + StrListsCol{}}, /*NULL*/ + null_at(5)} + .release(); + auto const results = cudf::interleave_columns(TView{{col1->view(), col2->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + +TYPED_TEST(ListsColumnsInterleaveTypedTest, SlicedColumnsInputNoNull) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col = ListsCol{{1, 2, 3}, {2, 3}, {3, 4, 5, 6}, {5, 6}, {}, {7}}.release(); + auto const col1 = cudf::slice(col->view(), {0, 3})[0]; + auto const col2 = cudf::slice(col->view(), {1, 4})[0]; + auto const col3 = cudf::slice(col->view(), {2, 5})[0]; + auto const col4 = cudf::slice(col->view(), {3, 6})[0]; + auto const expected = ListsCol{ + ListsCol{1, 2, 3}, + ListsCol{2, 3}, + ListsCol{3, 4, 5, 6}, + ListsCol{5, 6}, + ListsCol{2, 3}, + ListsCol{3, 4, 5, 6}, + ListsCol{5, 6}, + ListsCol{}, + ListsCol{3, 4, 5, 6}, + ListsCol{5, 6}, + ListsCol{}, + ListsCol{7}}.release(); + auto const results = cudf::interleave_columns(TView{{col1, col2, col3, col4}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + +TYPED_TEST(ListsColumnsInterleaveTypedTest, SlicedColumnsInputWithNulls) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col = ListsCol{{ListsCol{{null, 2, 3}, null_at(0)}, + ListsCol{2, 3}, /*NULL*/ + ListsCol{{3, null, 5, 6}, null_at(1)}, + ListsCol{5, 6}, /*NULL*/ + ListsCol{}, /*NULL*/ + ListsCol{7}, + ListsCol{8, 9, 10}}, + null_at({1, 3, 4})} + .release(); + auto const col1 = cudf::slice(col->view(), {0, 3})[0]; + auto const col2 = cudf::slice(col->view(), {1, 4})[0]; + auto const col3 = cudf::slice(col->view(), {2, 5})[0]; + auto const col4 = cudf::slice(col->view(), {3, 6})[0]; + auto const col5 = cudf::slice(col->view(), {4, 7})[0]; + auto const expected = ListsCol{{ListsCol{{null, 2, 3}, null_at(0)}, + ListsCol{}, /*NULL*/ + ListsCol{{3, null, 5, 6}, null_at(1)}, + ListsCol{}, /*NULL*/ + ListsCol{}, /*NULL*/ + ListsCol{}, /*NULL*/ + ListsCol{{3, null, 5, 6}, null_at(1)}, + ListsCol{}, /*NULL*/ + ListsCol{}, /*NULL*/ + ListsCol{7}, + ListsCol{{3, null, 5, 6}, null_at(1)}, + ListsCol{}, /*NULL*/ + ListsCol{}, /*NULL*/ + ListsCol{7}, + ListsCol{8, 9, 10}}, + null_at({1, 3, 4, 5, 7, 8, 11, 12})} + .release(); + auto const results = cudf::interleave_columns(TView{{col1, col2, col3, col4, col5}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + +TEST_F(ListsColumnsInterleaveTest, SlicedStringsColumnsInputWithNulls) +{ + auto const col = + StrListsCol{ + {StrListsCol{{"Tomato", "Bear" /*NULL*/, "Apple"}, null_at(1)}, + StrListsCol{{"Banana", "Pig" /*NULL*/, "Kiwi", "Cherry", "Whale" /*NULL*/}, null_at({1, 4})}, + StrListsCol{"Coconut"}, + StrListsCol{{"Orange", "Dog" /*NULL*/, "Fox" /*NULL*/, "Duck" /*NULL*/}, null_at({1, 2, 3})}, + StrListsCol{"Lemon", "Peach"}, + StrListsCol{{"Deer" /*NULL*/, "Snake" /*NULL*/, "Horse" /*NULL*/}, all_nulls()}}, /*NULL*/ + null_at(5)} + .release(); + auto const col1 = cudf::slice(col->view(), {0, 3})[0]; + auto const col2 = cudf::slice(col->view(), {1, 4})[0]; + auto const col3 = cudf::slice(col->view(), {2, 5})[0]; + auto const col4 = cudf::slice(col->view(), {3, 6})[0]; + auto const expected = + StrListsCol{ + {StrListsCol{{"Tomato", "" /*NULL*/, "Apple"}, null_at(1)}, + StrListsCol{{"Banana", "" /*NULL*/, "Kiwi", "Cherry", "" /*NULL*/}, null_at({1, 4})}, + StrListsCol{"Coconut"}, + StrListsCol{{"Orange", "" /*NULL*/, "" /*NULL*/, "" /*NULL*/}, null_at({1, 2, 3})}, + StrListsCol{{"Banana", "" /*NULL*/, "Kiwi", "Cherry", "" /*NULL*/}, null_at({1, 4})}, + StrListsCol{"Coconut"}, + StrListsCol{{"Orange", "" /*NULL*/, "" /*NULL*/, "" /*NULL*/}, null_at({1, 2, 3})}, + StrListsCol{"Lemon", "Peach"}, + StrListsCol{"Coconut"}, + StrListsCol{{"Orange", "" /*NULL*/, "" /*NULL*/, "" /*NULL*/}, null_at({1, 2, 3})}, + StrListsCol{"Lemon", "Peach"}, + StrListsCol{}}, /*NULL*/ + null_at(11)} + .release(); + auto const results = cudf::interleave_columns(TView{{col1, col2, col3, col4}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + CUDF_TEST_PROGRAM_MAIN() From ad081ae932614c9e3ff5c0e8632fbf49482ab862 Mon Sep 17 00:00:00 2001 From: Vyas Ramasubramani Date: Mon, 3 May 2021 13:14:44 -0700 Subject: [PATCH 21/65] Create a common code path for 1d Frames (#8115) This PR unifies many code paths for 1-dimensional frame objects, namely Index and Series types. The bulk of the PR is moving code around, but there are a few renames (in particular `Index._values`->`Index._column`) that are necessary for unifying the APIs as well as some additional removals of unnecessary functions. The unification also fixes a few bugs (for instance, `bool(cudf.Index(...))` should raise an exception but wasn't) and adds a few missing APIs that were present in one class but not the other. Authors: - Vyas Ramasubramani (https://github.com/vyasr) Approvers: - https://github.com/brandon-b-miller - Michael Wang (https://github.com/isVoid) URL: https://github.com/rapidsai/cudf/pull/8115 --- python/cudf/cudf/core/dataframe.py | 21 +-- python/cudf/cudf/core/frame.py | 213 ++++++++++++++++++++- python/cudf/cudf/core/index.py | 228 ++++------------------- python/cudf/cudf/core/indexing.py | 5 +- python/cudf/cudf/core/multiindex.py | 32 +--- python/cudf/cudf/core/series.py | 216 +-------------------- python/cudf/cudf/tests/test_dataframe.py | 6 - python/cudf/cudf/tests/test_index.py | 2 +- python/cudf/cudf/tests/test_pickling.py | 4 +- python/cudf/cudf/tests/test_series.py | 2 +- 10 files changed, 271 insertions(+), 458 deletions(-) diff --git a/python/cudf/cudf/core/dataframe.py b/python/cudf/cudf/core/dataframe.py index 131463e8871..f2be0e3bd6e 100644 --- a/python/cudf/cudf/core/dataframe.py +++ b/python/cudf/cudf/core/dataframe.py @@ -10,7 +10,7 @@ import warnings from collections import defaultdict from collections.abc import Iterable, Sequence -from typing import Any, Optional, Set, TypeVar +from typing import Any, Optional, TypeVar import cupy import numpy as np @@ -554,6 +554,7 @@ def _align_input_series_indices(data, index): return data, index + # The `constructor*` properties are used by `dask` (and `dask_cudf`) @property def _constructor(self): return DataFrame @@ -1456,7 +1457,7 @@ def _get_columns_by_label(self, labels, downcast=False): new_data, index=self.index, name=labels ) return out - out = self._constructor()._from_data( + out = self.__class__()._from_data( new_data, index=self.index, columns=new_data.to_pandas_index() ) return out @@ -3158,20 +3159,6 @@ def take(self, positions, keep_index=True): out.columns = self.columns return out - def __copy__(self): - return self.copy(deep=True) - - def __deepcopy__(self, memo=None): - """ - Parameters - ---------- - memo, default None - Standard signature. Unused - """ - if memo is None: - memo = {} - return self.copy(deep=True) - @annotate("INSERT", color="green", domain="cudf_python") def insert(self, loc, name, value): """ Add a column to DataFrame at the index specified by loc. @@ -7769,8 +7756,6 @@ def explode(self, column, ignore_index=False): return super()._explode(column, ignore_index) - _accessors = set() # type: Set[Any] - def from_pandas(obj, nan_as_null=None): """ diff --git a/python/cudf/cudf/core/frame.py b/python/cudf/cudf/core/frame.py index 5c4186c4ac7..4a434be42ce 100644 --- a/python/cudf/cudf/core/frame.py +++ b/python/cudf/cudf/core/frame.py @@ -48,6 +48,12 @@ class Frame(libcudf.table.Table): _data: "ColumnAccessor" + @classmethod + def __init_subclass__(cls): + # All subclasses contain a set _accessors that is used to hold custom + # accessors defined by user APIs (see cudf/api/extensions/accessor.py). + cls._accessors = set() + @classmethod def _from_table(cls, table: Frame): return cls(table._data, index=table._index) @@ -608,7 +614,7 @@ def _get_columns_by_index(self, indices): """ data = self._data.select_by_index(indices) - return self._constructor( + return self.__class__( data, columns=data.to_pandas_index(), index=self.index ) @@ -3295,6 +3301,211 @@ def _reindex( return self._mimic_inplace(result, inplace=inplace) +class SingleColumnFrame(Frame): + """A one-dimensional frame. + + Frames with only a single column share certain logic that is encoded in + this class. + """ + + @property + def name(self): + """The name of this object.""" + return next(iter(self._data.names)) + + @name.setter + def name(self, value): + self._data[value] = self._data.pop(self.name) + + @property + def ndim(self): + """Dimension of the data (always 1).""" + return 1 + + @property + def shape(self): + """Returns a tuple representing the dimensionality of the Index. + """ + return (len(self),) + + def __iter__(self): + cudf.utils.utils.raise_iteration_error(obj=self) + + def __len__(self): + return len(self._column) + + def __bool__(self): + raise TypeError( + f"The truth value of a {type(self)} is ambiguous. Use " + "a.empty, a.bool(), a.item(), a.any() or a.all()." + ) + + @property + def _column(self): + return self._data[self.name] + + @_column.setter + def _column(self, value): + self._data[self.name] = value + + @property + def values(self): + """ + Return a CuPy representation of the data. + + Returns + ------- + out : cupy.ndarray + A device representation of the underlying data. + + Examples + -------- + >>> import cudf + >>> ser = cudf.Series([1, -10, 100, 20]) + >>> ser.values + array([ 1, -10, 100, 20]) + >>> type(ser.values) + + >>> index = cudf.Index([1, -10, 100, 20]) + >>> index.values + array([ 1, -10, 100, 20]) + >>> type(index.values) + + """ + return self._column.values + + @property + def values_host(self): + """ + Return a NumPy representation of the data. + + Returns + ------- + out : numpy.ndarray + A host representation of the underlying data. + + Examples + -------- + >>> import cudf + >>> ser = cudf.Series([1, -10, 100, 20]) + >>> ser.values_host + array([ 1, -10, 100, 20]) + >>> type(ser.values_host) + + >>> index = cudf.Index([1, -10, 100, 20]) + >>> index.values_host + array([ 1, -10, 100, 20]) + >>> type(index.values_host) + + """ + return self._column.values_host + + def tolist(self): + + raise TypeError( + "cuDF does not support conversion to host memory " + "via the `tolist()` method. Consider using " + "`.to_arrow().to_pylist()` to construct a Python list." + ) + + to_list = tolist + + def to_gpu_array(self, fillna=None): + """Get a dense numba device array for the data. + + Parameters + ---------- + fillna : str or None + See *fillna* in ``.to_array``. + + Notes + ----- + + if ``fillna`` is ``None``, null values are skipped. Therefore, the + output size could be smaller. + + Returns + ------- + numba.DeviceNDArray + + Examples + -------- + >>> import cudf + >>> s = cudf.Series([10, 20, 30, 40, 50]) + >>> s + 0 10 + 1 20 + 2 30 + 3 40 + 4 50 + dtype: int64 + >>> s.to_gpu_array() + + """ + return self._column.to_gpu_array(fillna=fillna) + + @classmethod + def from_arrow(cls, array): + """Create from PyArrow Array/ChunkedArray. + + Parameters + ---------- + array : PyArrow Array/ChunkedArray + PyArrow Object which has to be converted. + + Raises + ------ + TypeError for invalid input type. + + Returns + ------- + SingleColumnFrame + + Examples + -------- + >>> import cudf + >>> import pyarrow as pa + >>> cudf.Index.from_arrow(pa.array(["a", "b", None])) + StringIndex(['a' 'b' None], dtype='object') + >>> cudf.Series.from_arrow(pa.array(["a", "b", None])) + 0 a + 1 b + 2 + dtype: object + """ + return cls(cudf.core.column.column.ColumnBase.from_arrow(array)) + + def to_arrow(self): + """ + Convert to a PyArrow Array. + + Returns + ------- + PyArrow Array + + Examples + -------- + >>> import cudf + >>> sr = cudf.Series(["a", "b", None]) + >>> sr.to_arrow() + + [ + "a", + "b", + null + ] + >>> ind = cudf.Index(["a", "b", None]) + >>> ind.to_arrow() + + [ + "a", + "b", + null + ] + """ + return self._column.to_arrow() + + def _get_replacement_values_for_columns( to_replace: Any, value: Any, columns_dtype_map: Dict[Any, Any] ) -> Tuple[Dict[Any, bool], Dict[Any, Any], Dict[Any, Any]]: diff --git a/python/cudf/cudf/core/index.py b/python/cudf/cudf/core/index.py index 0ffe0c11fef..5f390be79e2 100644 --- a/python/cudf/cudf/core/index.py +++ b/python/cudf/cudf/core/index.py @@ -4,7 +4,7 @@ import pickle from numbers import Number -from typing import Any, Dict, Set, Type +from typing import Any, Dict, Type import cupy import numpy as np @@ -29,7 +29,7 @@ ) from cudf.core.column.string import StringMethods as StringMethods from cudf.core.dtypes import IntervalDtype -from cudf.core.frame import Frame +from cudf.core.frame import SingleColumnFrame from cudf.utils import ioutils from cudf.utils.docutils import copy_docstring from cudf.utils.dtypes import ( @@ -45,35 +45,7 @@ from cudf.utils.utils import cached_property, search_range -def _to_frame(this_index, index=True, name=None): - """Create a DataFrame with a column containing this Index - - Parameters - ---------- - index : boolean, default True - Set the index of the returned DataFrame as the original Index - name : str, default None - Name to be used for the column - - Returns - ------- - DataFrame - cudf DataFrame - """ - - if name is not None: - col_name = name - elif this_index.name is None: - col_name = 0 - else: - col_name = this_index.name - - return cudf.DataFrame( - {col_name: this_index._values}, index=this_index if index else None - ) - - -class Index(Frame, Serializable): +class Index(SingleColumnFrame, Serializable): dtype: DtypeObj @@ -180,12 +152,6 @@ def drop_duplicates(self, keep="first"): """ # noqa: E501 return super().drop_duplicates(keep=keep) - @property - def shape(self): - """Returns a tuple representing the dimensionality of the Index. - """ - return (len(self),) - def serialize(self): header = {} header["index_column"] = {} @@ -277,81 +243,6 @@ def get_level_values(self, level): else: raise KeyError(f"Requested level with name {level} " "not found") - def __iter__(self): - cudf.utils.utils.raise_iteration_error(obj=self) - - @classmethod - def from_arrow(cls, array): - """Convert PyArrow Array/ChunkedArray to Index - - Parameters - ---------- - array : PyArrow Array/ChunkedArray - PyArrow Object which has to be converted to Index - - Raises - ------ - TypeError for invalid input type. - - Returns - ------- - cudf Index - - Examples - -------- - >>> import cudf - >>> import pyarrow as pa - >>> cudf.Index.from_arrow(pa.array(["a", "b", None])) - StringIndex(['a' 'b' None], dtype='object') - """ - - return cls(cudf.core.column.column.ColumnBase.from_arrow(array)) - - def to_arrow(self): - """Convert Index to PyArrow Array - - Returns - ------- - PyArrow Array - - Examples - -------- - >>> import cudf - >>> ind = cudf.Index(["a", "b", None]) - >>> ind.to_arrow() - - [ - "a", - "b", - null - ] - """ - - return self._data.columns[0].to_arrow() - - @property - def values_host(self): - """ - Return a numpy representation of the Index. - - Only the values in the Index will be returned. - - Returns - ------- - out : numpy.ndarray - The values of the Index. - - Examples - -------- - >>> import cudf - >>> index = cudf.Index([1, -10, 100, 20]) - >>> index.values_host - array([ 1, -10, 100, 20]) - >>> type(index.values_host) - - """ - return self._values.values_host - @classmethod def deserialize(cls, header, frames): h = header["index_column"] @@ -362,12 +253,6 @@ def deserialize(cls, header, frames): index = col_typ.deserialize(h, frames[: header["frame_count"]]) return idx_typ(index, name=name) - @property - def ndim(self): - """Dimension of the data. Apart from MultiIndex ndim is always 1. - """ - return 1 - @property def names(self): """ @@ -388,18 +273,6 @@ def names(self, values): self.name = values[0] - @property - def name(self): - """ - Returns the name of the Index. - """ - return next(iter(self._data.names)) - - @name.setter - def name(self, value): - col = self._data.pop(self.name) - self._data[value] = col - def dropna(self, how="any"): """ Return an Index with null values removed. @@ -641,25 +514,32 @@ def argsort(self, ascending=True, **kwargs): indices = self._values.argsort(ascending=ascending, **kwargs) return cupy.asarray(indices) - @property - def values(self): - """ - Return an array representing the data in the Index. + def to_frame(self, index=True, name=None): + """Create a DataFrame with a column containing this Index + + Parameters + ---------- + index : boolean, default True + Set the index of the returned DataFrame as the original Index + name : str, default None + Name to be used for the column Returns ------- - array : A cupy array of data in the Index. - - Examples - -------- - >>> import cudf - >>> index = cudf.Index([1, -10, 100, 20]) - >>> index.values - array([ 1, -10, 100, 20]) - >>> type(index.values) - + DataFrame + cudf DataFrame """ - return self._values.values + + if name is not None: + col_name = name + elif self.name is None: + col_name = 0 + else: + col_name = self.name + + return cudf.DataFrame( + {col_name: self._values}, index=self if index else None + ) def any(self): """ @@ -686,16 +566,6 @@ def to_pandas(self): """ return pd.Index(self._values.to_pandas(), name=self.name) - def tolist(self): - - raise TypeError( - "cuDF does not support conversion to host memory " - "via `tolist()` method. Consider using " - "`.to_arrow().to_pylist()` to construct a Python list." - ) - - to_list = tolist - @ioutils.doc_to_dlpack() def to_dlpack(self): """{docstring}""" @@ -1557,9 +1427,7 @@ def _from_table(cls, table): @classmethod def _from_data(cls, data, index=None): - return cls._from_table(Frame(data=data)) - - _accessors = set() # type: Set[Any] + return cls._from_table(SingleColumnFrame(data=data)) @property def _constructor_expanddim(self): @@ -1606,7 +1474,7 @@ def __new__( if step == 0: raise ValueError("Step must not be zero.") - out = Frame.__new__(cls) + out = SingleColumnFrame.__new__(cls) if isinstance(start, range): therange = start start = therange.start @@ -1815,7 +1683,7 @@ def is_contiguous(self): @property def size(self): - return self.__len__() + return len(self) def find_label_range(self, first=None, last=None): """Find subrange in the ``RangeIndex``, marked by their positions, that @@ -1856,25 +1724,6 @@ def find_label_range(self, first=None, last=None): return begin, end - @copy_docstring(_to_frame) # type: ignore - def to_frame(self, index=True, name=None): - return _to_frame(self, index, name) - - def to_gpu_array(self, fillna=None): - """Get a dense numba device array for the data. - - Parameters - ---------- - fillna : str or None - Replacement value to fill in place of nulls. - - Notes - ----- - if ``fillna`` is ``None``, null values are skipped. Therefore, the - output size could be smaller. - """ - return self._values.to_gpu_array(fillna=fillna) - def to_pandas(self): return pd.RangeIndex( start=self._start, @@ -1978,7 +1827,7 @@ def __new__(cls, values, **kwargs): Column's name. Otherwise if this name is different from the value Column's, the values Column will be cloned to adopt this name. """ - out = Frame.__new__(cls) + out = SingleColumnFrame.__new__(cls) out._initialize(values, **kwargs) return out @@ -2043,9 +1892,6 @@ def copy(self, name=None, deep=False, dtype=None, names=None): def __sizeof__(self): return self._values.__sizeof__() - def __len__(self): - return len(self._values) - def __repr__(self): max_seq_items = get_option("max_seq_items") or len(self) mr = 0 @@ -2135,10 +1981,6 @@ def __getitem__(self, index): else: return res - @copy_docstring(_to_frame) # type: ignore - def to_frame(self, index=True, name=None): - return _to_frame(self, index, name) - @property def dtype(self): """ @@ -2201,7 +2043,7 @@ class NumericIndex(GenericIndex): def __new__(cls, data=None, dtype=None, copy=False, name=None): - out = Frame.__new__(cls) + out = SingleColumnFrame.__new__(cls) dtype = _index_to_dtype[cls] if copy: data = column.as_column(data, dtype=dtype).copy() @@ -2323,7 +2165,7 @@ def __new__( # pandas dtindex creation first which. For now # just make sure we handle np.datetime64 arrays # and then just dispatch upstream - out = Frame.__new__(cls) + out = SingleColumnFrame.__new__(cls) if freq is not None: raise NotImplementedError("Freq is not yet supported") @@ -2578,7 +2420,7 @@ def __new__( name=None, ) -> "TimedeltaIndex": - out = Frame.__new__(cls) + out = SingleColumnFrame.__new__(cls) if freq is not None: raise NotImplementedError("freq is not yet supported") @@ -2710,7 +2552,7 @@ def __new__( ) if copy: data = column.as_column(data, dtype=dtype).copy(deep=True) - out = Frame.__new__(cls) + out = SingleColumnFrame.__new__(cls) kwargs = _setdefault_name(data, name=name) if isinstance(data, CategoricalColumn): data = data @@ -2936,7 +2778,7 @@ def __new__( ) -> "IntervalIndex": if copy: data = column.as_column(data, dtype=dtype).copy() - out = Frame.__new__(cls) + out = SingleColumnFrame.__new__(cls) kwargs = _setdefault_name(data, name=name) if isinstance(data, IntervalColumn): data = data @@ -3009,7 +2851,7 @@ class StringIndex(GenericIndex): """ def __new__(cls, values, copy=False, **kwargs): - out = Frame.__new__(cls) + out = SingleColumnFrame.__new__(cls) kwargs = _setdefault_name(values, **kwargs) if isinstance(values, StringColumn): values = values.copy(deep=copy) diff --git a/python/cudf/cudf/core/indexing.py b/python/cudf/cudf/core/indexing.py index 7970b9fa3dc..a732abc0705 100755 --- a/python/cudf/cudf/core/indexing.py +++ b/python/cudf/cudf/core/indexing.py @@ -2,6 +2,7 @@ from typing import Any, Union +import cupy as cp import numpy as np import pandas as pd from nvtx import annotate @@ -58,7 +59,9 @@ def get_label_range_or_mask(index, start, stop, step): if start is not None and stop is not None: if start > stop: return slice(0, 0, None) - boolean_mask = (index >= start) and (index <= stop) + # TODO: Once Index binary ops are updated to support logical_and, + # can use that instead of using cupy. + boolean_mask = cp.logical_and((index >= start), (index <= stop)) elif start is not None: boolean_mask = index >= start else: diff --git a/python/cudf/cudf/core/multiindex.py b/python/cudf/cudf/core/multiindex.py index a4748632aab..ca029198e52 100644 --- a/python/cudf/cudf/core/multiindex.py +++ b/python/cudf/cudf/core/multiindex.py @@ -19,7 +19,7 @@ from cudf.core._compat import PANDAS_GE_120 from cudf.core.column import column from cudf.core.column_accessor import ColumnAccessor -from cudf.core.frame import Frame +from cudf.core.frame import Frame, SingleColumnFrame from cudf.core.index import Index, as_index @@ -572,7 +572,7 @@ def from_arrow(cls, table): names=['a', 'b']) """ - return super(Index, cls).from_arrow(table) + return super(SingleColumnFrame, cls).from_arrow(table) def to_arrow(self): """Convert MultiIndex to PyArrow Table @@ -606,7 +606,7 @@ def to_arrow(self): ] """ - return super(Index, self).to_arrow() + return super(SingleColumnFrame, self).to_arrow() @property def codes(self): @@ -1048,9 +1048,6 @@ def deserialize(cls, header, frames): names = pickle.loads(header["names"]) return MultiIndex(names=names, source_data=source_data) - def __iter__(self): - cudf.utils.utils.raise_iteration_error(obj=self) - def __getitem__(self, index): # TODO: This should be a take of the _source_data only match = self.take(index) @@ -1107,29 +1104,6 @@ def get_level_values(self, level): ) return level_values - def _to_frame(self): - - # for each column of codes - # replace column with mapping from integers to levels - df = self.codes.copy(deep=False) - for idx, col in enumerate(df.columns): - # use merge as a replace fn - level = cudf.DataFrame( - { - "idx": column.arange( - len(self.levels[idx]), dtype=df[col].dtype - ), - "level": self.levels[idx], - } - ) - code = cudf.DataFrame({"idx": df[col]}) - df[col] = code.merge(level).level - return df - - @property - def _values(self): - return list([i for i in self]) - @classmethod def _concat(cls, objs): diff --git a/python/cudf/cudf/core/series.py b/python/cudf/cudf/core/series.py index 4cc5fb56a4c..5ee40d576b6 100644 --- a/python/cudf/cudf/core/series.py +++ b/python/cudf/cudf/core/series.py @@ -7,7 +7,7 @@ from collections import abc as abc from numbers import Number from shutil import get_terminal_size -from typing import Any, Optional, Set +from typing import Any, Optional from uuid import uuid4 import cupy @@ -38,7 +38,7 @@ from cudf.core.column.string import StringMethods from cudf.core.column.struct import StructMethods from cudf.core.column_accessor import ColumnAccessor -from cudf.core.frame import Frame, _drop_rows_by_labels +from cudf.core.frame import SingleColumnFrame, _drop_rows_by_labels from cudf.core.groupby.groupby import SeriesGroupBy from cudf.core.index import Index, RangeIndex, as_index from cudf.core.indexing import _SeriesIlocIndexer, _SeriesLocIndexer @@ -61,7 +61,8 @@ ) -class Series(Frame, Serializable): +class Series(SingleColumnFrame, Serializable): + # The `constructor*` properties are used by `dask` (and `dask_cudf`) @property def _constructor(self): return Series @@ -265,8 +266,7 @@ def __init__( @classmethod def _from_table(cls, table, index=None): - name = next(iter(table._data.keys())) - data = next(iter(table._data.values())) + name, data = next(iter(table._data.items())) if index is None: if table._index is not None: index = Index._from_table(table._index) @@ -289,14 +289,6 @@ def _from_data( out.name = name return out - @property - def _column(self): - return self._data[self.name] - - @_column.setter - def _column(self, value): - self._data[self.name] = value - def __contains__(self, item): return item in self._index @@ -341,52 +333,6 @@ def from_pandas(cls, s, nan_as_null=None): """ return cls(s, nan_as_null=nan_as_null) - @property - def values(self): - """ - Return a CuPy representation of the Series. - - Only the values in the Series will be returned. - - Returns - ------- - out : cupy.ndarray - The values of the Series. - - Examples - -------- - >>> import cudf - >>> ser = cudf.Series([1, -10, 100, 20]) - >>> ser.values - array([ 1, -10, 100, 20]) - >>> type(ser.values) - - """ - return self._column.values - - @property - def values_host(self): - """ - Return a numpy representation of the Series. - - Only the values in the Series will be returned. - - Returns - ------- - out : numpy.ndarray - The values of the Series. - - Examples - -------- - >>> import cudf - >>> ser = cudf.Series([1, -10, 100, 20]) - >>> ser.values_host - array([ 1, -10, 100, 20]) - >>> type(ser.values_host) - - """ - return self._column.values_host - def serialize(self): header = {} frames = [] @@ -401,12 +347,6 @@ def serialize(self): return header, frames - @property - def shape(self): - """Returns a tuple representing the dimensionality of the Series. - """ - return (len(self),) - @property def dt(self): """ @@ -435,23 +375,6 @@ def dt(self): "Can only use .dt accessor with datetimelike values" ) - @property - def ndim(self): - """Dimension of the data. Series ndim is always 1. - """ - return 1 - - @property - def name(self): - """Returns name of the Series. - """ - return self._data.names[0] - - @name.setter - def name(self, value): - col = self._data.pop(self.name) - self._data[value] = col - @classmethod def deserialize(cls, header, frames): index_nframes = header["index_frame_count"] @@ -487,64 +410,11 @@ def _get_columns_by_label(self, labels, downcast=False): new_data = super()._get_columns_by_label(labels, downcast) return ( - self._constructor(data=new_data, index=self.index) + self.__class__(data=new_data, index=self.index) if len(new_data) > 0 - else self._constructor(dtype=self.dtype, name=self.name) + else self.__class__(dtype=self.dtype, name=self.name) ) - @classmethod - def from_arrow(cls, array): - """ - Convert from PyArrow Array/ChunkedArray to Series. - - Parameters - ---------- - array : PyArrow Array/ChunkedArray - PyArrow Object which has to be converted to cudf Series. - - Raises - ------ - TypeError for invalid input type. - - Returns - ------- - cudf Series - - Examples - -------- - >>> import cudf - >>> import pyarrow as pa - >>> cudf.Series.from_arrow(pa.array(["a", "b", None])) - 0 a - 1 b - 2 - dtype: object - """ - - return cls(cudf.core.column.ColumnBase.from_arrow(array)) - - def to_arrow(self): - """ - Convert Series to a PyArrow Array. - - Returns - ------- - PyArrow Array - - Examples - -------- - >>> import cudf - >>> sr = cudf.Series(["a", "b", None]) - >>> sr.to_arrow() - - [ - "a", - "b", - null - ] - """ - return self._column.to_arrow() - def drop( self, labels=None, @@ -667,14 +537,6 @@ def drop( if not inplace: return out - def __copy__(self, deep=True): - return self.copy(deep) - - def __deepcopy__(self, memo=None): - if memo is None: - memo = {} - return self.copy() - def append(self, to_append, ignore_index=False, verify_integrity=False): """Append values from another ``Series`` or array-like object. If ``ignore_index=True``, the index is reset. @@ -1047,11 +909,6 @@ def memory_usage(self, index=True, deep=False): n += self._index.memory_usage(deep=deep) return n - def __len__(self): - """Returns the size of the ``Series`` including null values. - """ - return len(self._column) - def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): if method == "__call__": return get_appropriate_dispatched_func( @@ -1186,12 +1043,9 @@ def __getitem__(self, arg): else: return self.loc[arg] - def __iter__(self): - cudf.utils.utils.raise_iteration_error(obj=self) - - iteritems = __iter__ + iteritems = SingleColumnFrame.__iter__ - items = __iter__ + items = SingleColumnFrame.__iter__ def to_dict(self, into=dict): raise TypeError( @@ -1251,22 +1105,6 @@ def take(self, indices, keep_index=True): data = self._column.take(col_inds, keep_index=False) return self._copy_construct(data=data, index=None) - def __bool__(self): - """Always raise TypeError when converting a Series - into a boolean. - """ - raise TypeError(f"can't compute boolean for {type(self)}") - - def tolist(self): - - raise TypeError( - "cuDF does not support conversion to host memory " - "via `tolist()` method. Consider using " - "`.to_arrow().to_pylist()` to construct a Python list." - ) - - to_list = tolist - def head(self, n=5): """ Return the first `n` rows. @@ -3149,40 +2987,6 @@ def any(self, axis=0, bool_only=None, skipna=True, level=None, **kwargs): return result_series._column.any() - def to_gpu_array(self, fillna=None): - """Get a dense numba device array for the data. - - Parameters - ---------- - fillna : str or None - See *fillna* in ``.to_array``. - - Notes - ----- - - if ``fillna`` is ``None``, null values are skipped. Therefore, the - output size could be smaller. - - Returns - ------- - numba DeviceNDArray - - Examples - -------- - >>> import cudf - >>> s = cudf.Series([10, 20, 30, 40, 50]) - >>> s - 0 10 - 1 20 - 2 30 - 3 40 - 4 50 - dtype: int64 - >>> s.to_gpu_array() - - """ - return self._column.to_gpu_array(fillna=fillna) - def to_pandas(self, index=True, nullable=False, **kwargs): """ Convert to a Pandas Series. @@ -6527,8 +6331,6 @@ def explode(self, ignore_index=False): return super()._explode(self._column_names[0], ignore_index) - _accessors = set() # type: Set[Any] - truediv_int_dtype_corrections = { "int8": "float32", diff --git a/python/cudf/cudf/tests/test_dataframe.py b/python/cudf/cudf/tests/test_dataframe.py index 4890ccc289e..e5e36ba7e21 100644 --- a/python/cudf/cudf/tests/test_dataframe.py +++ b/python/cudf/cudf/tests/test_dataframe.py @@ -4377,12 +4377,6 @@ def test_constructor_properties(): df[key1] = val1 df[key2] = val2 - # Correct use of _constructor (for DataFrame) - assert_eq(df, df._constructor({key1: val1, key2: val2})) - - # Correct use of _constructor (for cudf.Series) - assert_eq(df[key1], df[key2]._constructor(val1, name=key1)) - # Correct use of _constructor_sliced (for DataFrame) assert_eq(df[key1], df._constructor_sliced(val1, name=key1)) diff --git a/python/cudf/cudf/tests/test_index.py b/python/cudf/cudf/tests/test_index.py index 21a431dd540..158dffc3884 100644 --- a/python/cudf/cudf/tests/test_index.py +++ b/python/cudf/cudf/tests/test_index.py @@ -1799,7 +1799,7 @@ def test_index_tolist(data, dtype): TypeError, match=re.escape( r"cuDF does not support conversion to host memory " - r"via `tolist()` method. Consider using " + r"via the `tolist()` method. Consider using " r"`.to_arrow().to_pylist()` to construct a Python list." ), ): diff --git a/python/cudf/cudf/tests/test_pickling.py b/python/cudf/cudf/tests/test_pickling.py index e87ab3730dd..ca819c7f59b 100644 --- a/python/cudf/cudf/tests/test_pickling.py +++ b/python/cudf/cudf/tests/test_pickling.py @@ -90,7 +90,9 @@ def test_pickle_index(): idx = GenericIndex(np.arange(nelem), name="a") pickled = pickle.dumps(idx) out = pickle.loads(pickled) - assert idx == out + # TODO: Once operations like `all` are supported on Index objects, we can + # just use that without calling values first. + assert (idx == out).values.all() def test_pickle_buffer(): diff --git a/python/cudf/cudf/tests/test_series.py b/python/cudf/cudf/tests/test_series.py index 0dc53fa29e9..0cc0ad57745 100644 --- a/python/cudf/cudf/tests/test_series.py +++ b/python/cudf/cudf/tests/test_series.py @@ -372,7 +372,7 @@ def test_series_tolist(data): TypeError, match=re.escape( r"cuDF does not support conversion to host memory " - r"via `tolist()` method. Consider using " + r"via the `tolist()` method. Consider using " r"`.to_arrow().to_pylist()` to construct a Python list." ), ): From 36eaa0681d190200b82866f3dcd8d6b3963b9f48 Mon Sep 17 00:00:00 2001 From: Vibhu Jawa Date: Mon, 3 May 2021 13:43:52 -0700 Subject: [PATCH 22/65] Subword Tokenizer HuggingFace like API (#7942) This PR closes https://github.com/rapidsai/cudf/issues/5868 by adding a new tokenizer API. We are seeing speedups even at low batch sizes (10/100) so this should potentially unlock some inference/training use cases for us. ## Benchmarks: (Thanks to @davidwendt for writing the super fast tokenizer :boom: ) | Batch Size | HuggingFace | Rapids Old API | Rapids Tokenizer API | Tokenizer API Speed up vs HuggingFace | Rapids New API Speedup | |- |- |- |- |- |- | | 1 | 0.000242 | 0.006890 | 0.000497 | 0.487 | 13.863 | | 10 | 0.002800 | 0.007030 | 0.000516 | 5.426 | 13.624 | | 100 | 0.016200 | 0.007140 | 0.000537 | 30.168 | 13.296 | | 1000 | 0.149000 | 0.007150 | 0.000517 | 288.201 | 13.830 | ## API Comparision to HuggingFace: The goal of this PR is to ensure our API matches up HuggingFace as much as possible to help with ease of porting. Proposed API in this PR: ```python from cudf.core.subword_tokenizer import SubwordTokenizer tokenizer = SubwordTokenizer('bert-base-cased-vocab-hash.txt',do_lower_case=False) output = tokenizer(str_series, max_num_rows=len(str_series), truncation=True, max_length=seq_len, padding='max_length', add_special_tokens=False, return_tensors='pt') ``` HuggingFace API: ```python from transformers import BertTokenizerFast tokenizer = BertTokenizerFast.from_pretrained('bert-base-cased', do_lower_case=False) output = tokenizer(input_sentence_ls, truncation=True, max_length=seq_len, padding='max_length', add_special_tokens=False, return_tensors = 'pt') output_d = {k:v.cuda() for k,v in output.items()} ``` ## TODO: - [x] Add tests - [x] Throw appropriate warnings for HuggingFace discrepancies - [x] API checks - [X] [ Benchmark/Example Notebook ](https://nbviewer.jupyter.org/gist/VibhuJawa/350a8479b10be3591dd9c4d5da3cfc3b) CC: @raykallen, @BartleyR (from the cyber team) CC: @randerzander , @beckernick (from the workflows team) Authors: - Vibhu Jawa (https://github.com/VibhuJawa) Approvers: - GALI PREM SAGAR (https://github.com/galipremsagar) - AJ Schmidt (https://github.com/ajschmidt8) - Keith Kraus (https://github.com/kkraus14) URL: https://github.com/rapidsai/cudf/pull/7942 --- conda/environments/cudf_dev_cuda11.0.yml | 1 + conda/environments/cudf_dev_cuda11.1.yml | 1 + conda/environments/cudf_dev_cuda11.2.yml | 1 + docs/cudf/source/api.rst | 7 + .../cudf/_lib/cpp/nvtext/subword_tokenize.pxd | 28 +- .../cudf/_lib/nvtext/subword_tokenize.pyx | 58 +- python/cudf/cudf/core/column/string.py | 4 +- python/cudf/cudf/core/subword_tokenizer.py | 295 ++ .../bert_base_cased_sampled/vocab-hash.txt | 4382 +++++++++++++++++ .../bert_base_cased_sampled/vocab.txt | 3500 +++++++++++++ .../subword_tokenizer_data/test_sentences.txt | 100 + .../bert-base-uncased-vocab-5per.txt | 2475 ---------- .../ground_truth_vocab_hash_5per.txt | 3100 ------------ python/cudf/cudf/tests/test_hash_vocab.py | 16 +- .../cudf/cudf/tests/test_subword_tokenizer.py | 112 + .../cuda-11.0/dev_requirements.txt | 2 +- .../cuda-11.1/dev_requirements.txt | 2 +- .../cuda-11.2/dev_requirements.txt | 2 +- python/cudf/setup.py | 1 + 19 files changed, 8491 insertions(+), 5596 deletions(-) create mode 100644 python/cudf/cudf/core/subword_tokenizer.py create mode 100644 python/cudf/cudf/tests/data/subword_tokenizer_data/bert_base_cased_sampled/vocab-hash.txt create mode 100644 python/cudf/cudf/tests/data/subword_tokenizer_data/bert_base_cased_sampled/vocab.txt create mode 100644 python/cudf/cudf/tests/data/subword_tokenizer_data/test_sentences.txt delete mode 100644 python/cudf/cudf/tests/data/vocab_hash/bert-base-uncased-vocab-5per.txt delete mode 100644 python/cudf/cudf/tests/data/vocab_hash/ground_truth_vocab_hash_5per.txt create mode 100644 python/cudf/cudf/tests/test_subword_tokenizer.py diff --git a/conda/environments/cudf_dev_cuda11.0.yml b/conda/environments/cudf_dev_cuda11.0.yml index bb2b92cbbaf..7c9fdc318c1 100644 --- a/conda/environments/cudf_dev_cuda11.0.yml +++ b/conda/environments/cudf_dev_cuda11.0.yml @@ -59,6 +59,7 @@ dependencies: - protobuf - nvtx>=0.2.1 - cachetools + - transformers - pip: - git+https://github.com/dask/dask.git@main - git+https://github.com/dask/distributed.git@main diff --git a/conda/environments/cudf_dev_cuda11.1.yml b/conda/environments/cudf_dev_cuda11.1.yml index 32e95123159..e278454d371 100644 --- a/conda/environments/cudf_dev_cuda11.1.yml +++ b/conda/environments/cudf_dev_cuda11.1.yml @@ -59,6 +59,7 @@ dependencies: - protobuf - nvtx>=0.2.1 - cachetools + - transformers - pip: - git+https://github.com/dask/dask.git@main - git+https://github.com/dask/distributed.git@main diff --git a/conda/environments/cudf_dev_cuda11.2.yml b/conda/environments/cudf_dev_cuda11.2.yml index 927a1ea12e0..02eb40893dc 100644 --- a/conda/environments/cudf_dev_cuda11.2.yml +++ b/conda/environments/cudf_dev_cuda11.2.yml @@ -59,6 +59,7 @@ dependencies: - protobuf - nvtx>=0.2.1 - cachetools + - transformers - pip: - git+https://github.com/dask/dask.git@main - git+https://github.com/dask/distributed.git@main diff --git a/docs/cudf/source/api.rst b/docs/cudf/source/api.rst index b4ca0321073..d3042be2129 100644 --- a/docs/cudf/source/api.rst +++ b/docs/cudf/source/api.rst @@ -206,6 +206,13 @@ Window .. autoclass:: Rolling :members: +SubwordTokenizer +---------------- +.. currentmodule:: cudf.core.subword_tokenizer + +.. autoclass:: SubwordTokenizer + :members: + :special-members: __call__ General utility functions ------------------------- diff --git a/python/cudf/cudf/_lib/cpp/nvtext/subword_tokenize.pxd b/python/cudf/cudf/_lib/cpp/nvtext/subword_tokenize.pxd index 3df0bbc0815..013ce9de8f4 100644 --- a/python/cudf/cudf/_lib/cpp/nvtext/subword_tokenize.pxd +++ b/python/cudf/cudf/_lib/cpp/nvtext/subword_tokenize.pxd @@ -3,7 +3,8 @@ from libcpp cimport bool from libcpp.memory cimport unique_ptr from libcpp.string cimport string -from libc.stdint cimport uint32_t +from libc.stdint cimport uint16_t, uint32_t + from cudf._lib.cpp.column.column cimport column from cudf._lib.cpp.column.column_view cimport column_view @@ -17,6 +18,31 @@ cdef extern from "nvtext/subword_tokenize.hpp" namespace "nvtext" nogil: unique_ptr[column] tensor_attention_mask unique_ptr[column] tensor_metadata + cdef struct hashed_vocabulary "nvtext::hashed_vocabulary": + uint16_t first_token_id + uint16_t separator_token_id + uint16_t unknown_token_id + uint32_t outer_hash_a + uint32_t outer_hash_b + uint16_t num_bin + unique_ptr[column] table + unique_ptr[column] bin_coefficients + unique_ptr[column] bin_offsets + + cdef unique_ptr[hashed_vocabulary] load_vocabulary_file( + const string &filename_hashed_vocabulary + ) except + + + cdef tokenizer_result subword_tokenize( + const column_view & strings, + hashed_vocabulary & hashed_vocablary_obj, + uint32_t max_sequence_length, + uint32_t stride, + bool do_lower, + bool do_truncate, + uint32_t max_rows_tensor + ) except + + cdef tokenizer_result subword_tokenize( const column_view &strings, const string &filename_hashed_vocabulary, diff --git a/python/cudf/cudf/_lib/nvtext/subword_tokenize.pyx b/python/cudf/cudf/_lib/nvtext/subword_tokenize.pyx index 9e1d73326fc..3cf3cbe1ef2 100644 --- a/python/cudf/cudf/_lib/nvtext/subword_tokenize.pyx +++ b/python/cudf/cudf/_lib/nvtext/subword_tokenize.pyx @@ -9,27 +9,74 @@ from libc.stdint cimport uintptr_t from cudf._lib.cpp.column.column cimport column from cudf._lib.cpp.column.column_view cimport column_view -from cudf._lib.cpp.nvtext.subword_tokenize cimport ( +from cudf._lib.cpp.nvtext.subword_tokenize cimport( subword_tokenize as cpp_subword_tokenize, + hashed_vocabulary as cpp_hashed_vocabulary, + load_vocabulary_file as cpp_load_vocabulary_file, tokenizer_result as cpp_tokenizer_result, - move as tr_move + move as tr_move, ) from cudf._lib.column cimport Column -def subword_tokenize( +cdef class Hashed_Vocabulary: + cdef unique_ptr[cpp_hashed_vocabulary] c_obj + + def __cinit__(self, hash_file): + cdef string c_hash_file = str(hash_file).encode() + with nogil: + self.c_obj = move(cpp_load_vocabulary_file(c_hash_file)) + + +def subword_tokenize_inmem_hash( Column strings, - object hash_file, + Hashed_Vocabulary hashed_vocabulary, uint32_t max_sequence_length=64, uint32_t stride=48, bool do_lower=True, bool do_truncate=False, uint32_t max_rows_tensor=500 ): + """ + Subword tokenizes text series by using the pre-loaded hashed vocabulary + """ cdef column_view c_strings = strings.view() - cdef string c_hash_file = str(hash_file).encode() cdef cpp_tokenizer_result c_result + with nogil: + c_result = tr_move( + cpp_subword_tokenize( + c_strings, + hashed_vocabulary.c_obj.get()[0], + max_sequence_length, + stride, + do_lower, + do_truncate, + max_rows_tensor + ) + ) + # return the 3 tensor components + tokens = Column.from_unique_ptr(move(c_result.tensor_token_ids)) + masks = Column.from_unique_ptr(move(c_result.tensor_attention_mask)) + metadata = Column.from_unique_ptr(move(c_result.tensor_metadata)) + return tokens, masks, metadata + +def subword_tokenize_vocab_file( + Column strings, + object hash_file, + uint32_t max_sequence_length=64, + uint32_t stride=48, + bool do_lower=True, + bool do_truncate=False, + uint32_t max_rows_tensor=500 +): + """ + Subword tokenizes text series by using the hashed vocabulary + stored on disk + """ + cdef column_view c_strings = strings.view() + cdef cpp_tokenizer_result c_result + cdef string c_hash_file = str(hash_file).encode() with nogil: c_result = tr_move( cpp_subword_tokenize( @@ -42,7 +89,6 @@ def subword_tokenize( max_rows_tensor ) ) - # return the 3 tensor components tokens = Column.from_unique_ptr(move(c_result.tensor_token_ids)) masks = Column.from_unique_ptr(move(c_result.tensor_attention_mask)) diff --git a/python/cudf/cudf/core/column/string.py b/python/cudf/cudf/core/column/string.py index e4d7b6d4188..044088b68b5 100644 --- a/python/cudf/cudf/core/column/string.py +++ b/python/cudf/cudf/core/column/string.py @@ -41,7 +41,7 @@ porter_stemmer_measure as cpp_porter_stemmer_measure, ) from cudf._lib.nvtext.subword_tokenize import ( - subword_tokenize as cpp_subword_tokenize, + subword_tokenize_vocab_file as cpp_subword_tokenize_vocab_file, ) from cudf._lib.nvtext.tokenize import ( _count_tokens_column as cpp_count_tokens_column, @@ -4617,7 +4617,7 @@ def subword_tokenize( array([[0, 0, 2], [1, 0, 1]], dtype=uint32) """ - tokens, masks, metadata = cpp_subword_tokenize( + tokens, masks, metadata = cpp_subword_tokenize_vocab_file( self._column, hash_file, max_length, diff --git a/python/cudf/cudf/core/subword_tokenizer.py b/python/cudf/cudf/core/subword_tokenizer.py new file mode 100644 index 00000000000..9058491d8e7 --- /dev/null +++ b/python/cudf/cudf/core/subword_tokenizer.py @@ -0,0 +1,295 @@ +# Copyright (c) 2021, NVIDIA CORPORATION. + +from __future__ import annotations +from typing import Union +import cupy as cp +from warnings import warn + +from cudf._lib.nvtext.subword_tokenize import ( + subword_tokenize_inmem_hash as cpp_subword_tokenize, + Hashed_Vocabulary as cpp_hashed_vocabulary, +) + + +def _cast_to_appropriate_type(ar, cast_type): + if cast_type == "cp": + return ar + + if cast_type == "pt": + from torch.utils.dlpack import from_dlpack + + elif cast_type == "tf": + from tf.experimental.dlpack import from_dlpack + + return from_dlpack(ar.astype("int32").toDlpack()) + + +class SubwordTokenizer: + """ + Run CUDA BERT subword tokenizer on cuDF strings column. + Encodes words to token ids using vocabulary from a pretrained + tokenizer. + This function requires about 21x the number of character bytes + in the input strings column as working memory. + + Parameters + ---------- + hash_file : str + Path to hash file containing vocabulary of words with token-ids. + This can be created from the raw vocabulary + using the ``cudf.utils.hash_vocab_utils.hash_vocab`` function + + do_lower : bool, Default is True + If set to True, original text will be lowercased before encoding. + + Returns + ------- + SubwordTokenizer + """ + + def __init__(self, hash_file: str, do_lower_case: bool = True): + + self.do_lower_case = do_lower_case + self.vocab_file = cpp_hashed_vocabulary(hash_file) + + def __call__( + self, + text, + max_length: int, + max_num_rows: int, + add_special_tokens: bool = True, + padding: str = "max_length", + truncation: Union[bool, str] = False, + stride: int = 0, + return_tensors: str = "cp", + return_token_type_ids: bool = False, + ): + """ + Run CUDA BERT subword tokenizer on cuDF strings column. + Encodes words to token ids using vocabulary from a + pretrained tokenizer. + + Parameters + ---------- + text : cudf string series + The batch of sequences to be encoded. + + max_length : int + Controls the maximum length to use or pad to. + + max_num_rows : int + Maximum number of rows for the output token-ids expected to + be generated by the tokenizer. + Used for allocating temporary working memory on the GPU device. + If the output generates a larger number of rows, + behavior is undefined. + This will vary based on stride, truncation, and max_length. + For example, for non-overlapping sequences output rows will be + the same as input rows. + A good default can be twice the max_length + + add_special_tokens : bool, optional, defaults to True + Whether or not to encode the sequences with the special tokens + of the BERT classification model + + padding : "max_length" + Pad to a maximum length specified with the argument max_length + + truncation : bool, defaults to False + True: + Truncate to a maximum length specified with the argument max_length + False or 'do_not_truncate': default + No truncation (Output differs from HuggingFace) + + stride : int, optional, defaults to 0 + The value of this argument defines the number of + overlapping tokens. + The information about the overlapping tokens is + present in the metadata outputed. + + return_tensors : str, {"cp", "pt", "tf"} defaults to "cp" + "cp" : Return cupy cp.ndarray objects + "tf" : Return TensorFlow tf.constant objects + "pt" : Return PyTorch torch.Tensor objects + + + return_token_type_ids : bool, optional + Only False currently supported + + Returns + ------- + An encoding with the following fields: + input_ids:(type defined by return_tensors) + A tensor of token ids to be fed to the model. + attention_mask: (type defined by return_tensors) + A tensor of indices specifying which tokens + should be attended to by the model + metadata: (type defined by return_tensors) + Each row contains the index id of the original string and the + first and last index of the token-ids that are non-padded and + non-overlapping + + Examples + -------- + >>> import cudf + >>> from cudf.utils.hash_vocab_utils import hash_vocab + >>> hash_vocab('bert-base-cased-vocab.txt', 'voc_hash.txt') + + + >>> from cudf.core.subword_tokenizer import SubwordTokenizer + >>> cudf_tokenizer = SubwordTokenizer('voc_hash.txt', + ... do_lower_case=True) + >>> str_series = cudf.Series(['This is the', 'best book']) + >>> tokenizer_output = cudf_tokenizer(str_series, + ... max_length=8, + ... max_num_rows=len(str_series), + ... padding='max_length', + ... return_tensors='pt', + ... truncation=True) + >>> tokenizer_output['input_ids'] + tensor([[ 101, 1142, 1110, 1103, 102, 0, 0, 0], + [ 101, 1436, 1520, 102, 0, 0, 0, 0]], + device='cuda:0', + dtype=torch.int32) + >>> tokenizer_output['attention_mask'] + tensor([[1, 1, 1, 1, 1, 0, 0, 0], + [1, 1, 1, 1, 0, 0, 0, 0]], + device='cuda:0', dtype=torch.int32) + >>> tokenizer_output['metadata'] + tensor([[0, 1, 3], + [1, 1, 2]], device='cuda:0', dtype=torch.int32) + """ + + if return_token_type_ids: + # raise not currently supported + # Can also return zeros + error_msg = "Returning token_type_ids is currently supported" + raise NotImplementedError(error_msg) + + if truncation in (False, "do_not_truncate"): + if add_special_tokens: + error_msg = ( + "Adding special tokens is not supported " + f"with truncation = {truncation}. " + ) + recommendation = ( + "Custom Cupy kernel can potentially " + "be used to add it. For reference " + "see: _bert_add_special_tokens" + ) + raise NotImplementedError(error_msg + recommendation) + + truncation = False + warning_msg = ( + "When truncation is not True, the behaviour currently differs " + "from HuggingFace as cudf always returns overflowing tokens" + ) + warn(warning_msg) + + if padding != "max_length": + error_msg = ( + "Only padding to the provided max_length" + "is currently supported" + ) + raise NotImplementedError(error_msg) + + if max_length <= stride: + error_msg = "Stride should be less than max_length" + raise ValueError(error_msg) + + if return_tensors not in {"cp", "pt", "tf"}: + error_msg = ( + "Only cupy(cp), pytorch(pt) and tensorflow(tf) " + "tensors are supported" + ) + raise NotImplementedError(error_msg) + + stride = max_length - stride + # behaviour varies from subword_tokenize but maps with huggingface + + input_ids, attention_mask, metadata = cpp_subword_tokenize( + text._column, + self.vocab_file, + max_sequence_length=max_length, + stride=stride, + do_lower=self.do_lower_case, + do_truncate=truncation, + max_rows_tensor=max_num_rows, + ) + + tokenizer_output = { + "input_ids": cp.asarray(input_ids).reshape(-1, max_length), + "attention_mask": cp.asarray(attention_mask).reshape( + -1, max_length + ), + "metadata": cp.asarray(metadata).reshape(-1, 3), + } + + if add_special_tokens: + tokenizer_output = _bert_add_special_tokens(tokenizer_output) + + tokenizer_output = { + k: _cast_to_appropriate_type(v, return_tensors) + for k, v in tokenizer_output.items() + } + + return tokenizer_output + + +def _bert_add_special_tokens(token_o): + """ + Adds special tokens (CLS,SEP) which are often used by pre-trained BERT + models to input_ids and adjusts attention_mask and metadata to account + for them. + """ + max_length = token_o["input_ids"].shape[1] + seq_end_col = max_length - (token_o["input_ids"][:, ::-1] != 0).argmax(1) + # clipping to take overflow into account + seq_end_col = cp.clip(seq_end_col + 1, a_max=max_length - 1) + + _bert_add_special_tokens_input_ids(token_o["input_ids"], seq_end_col) + _bert_add_special_tokens_attention_mask( + token_o["attention_mask"], seq_end_col + ) + _bert_add_special_tokens_metadata(token_o["metadata"], max_length) + + return token_o + + +def _bert_add_special_tokens_input_ids(input_ids, seq_end_col): + """ + Add token ids for special tokens ([CLS] and [SEP]) to + the start and end of each sequence + """ + # Mark sequence start with [CLS] token mapping to the start of sequence + input_ids[:, 1:-1] = input_ids[:, 0:-2] + input_ids[:, 0] = 101 + # Mark end of sequence [SEP] + + input_ids[ + cp.arange(0, input_ids.shape[0], dtype=cp.uint32), seq_end_col + ] = 102 + + +def _bert_add_special_tokens_attention_mask(attention_mask, seq_end_col): + """ + Mark attention mask for special tokens ([CLS] and [SEP]) with 1 + """ + # Copy attention masks for all but last two + attention_mask[:, 1:-1] = attention_mask[:, 0:-2] + # Mark [CLS] token with 1 + attention_mask[:, 0] = 1 + # Mark [SEP] token with 1 + attention_mask[ + cp.arange(0, attention_mask.shape[0], dtype=cp.uint32), seq_end_col + ] = 1 + + +def _bert_add_special_tokens_metadata(metadata, max_length): + """ + Edit metadata to account for the added special tokens ([CLS] and [SEP]) + """ + # metadata seq starts from plus 1 + metadata[:, 1] = metadata[:, 1] + 1 + # clip done to take overflow into account + metadata[:, 2] = cp.clip(metadata[:, 2] + 1, a_max=max_length - 2) diff --git a/python/cudf/cudf/tests/data/subword_tokenizer_data/bert_base_cased_sampled/vocab-hash.txt b/python/cudf/cudf/tests/data/subword_tokenizer_data/bert_base_cased_sampled/vocab-hash.txt new file mode 100644 index 00000000000..84b13c9d946 --- /dev/null +++ b/python/cudf/cudf/tests/data/subword_tokenizer_data/bert_base_cased_sampled/vocab-hash.txt @@ -0,0 +1,4382 @@ +26899 +27424 +875 +7428432802425011718 0 +5054974408289448963 6 +18358444369622338053 9 +5716902217424485892 14 +8236612966193239043 18 +15282833726017872390 21 +15533348956988973570 27 +9001315167781089284 29 +7621090240282984451 33 +15337888141402371590 36 +16169070283077377537 42 +15615300272936709634 43 +12338784885023498756 45 +3175624061711419395 49 +9436392785812228615 52 +12978641027296058883 59 +14468815760709033991 62 +15607694490571932163 69 +53295083356623878 72 +0 78 +2230148770582976004 78 +6120456721458209796 82 +15411373208619074054 86 +10274574020114097153 92 +9000294930530661890 93 +13031557903172483076 95 +11350066664294002181 99 +6325605033787362307 104 +2909954277284188676 107 +4104562716099355138 111 +3267092979937387012 113 +17525453481571210244 117 +11532627846208440834 121 +10784672185103672321 123 +11229796758348255749 124 +4379577250247562242 129 +1041161126836283908 131 +3854383966527313413 135 +16467720483237810694 140 +14820844471735454722 146 +13111220924289178119 148 +2548683052821249538 155 +719749806464434178 157 +2121722119826170883 159 +9005614210949580292 162 +7050169108294333445 166 +17351764915062575107 171 +14644698505496219141 174 +11657834349296686081 179 +13626797927783164930 180 +14735048589438940164 182 +1078491261937017863 186 +7952761372439242754 193 +7692446865301965827 195 +4552111108816020995 198 +12455022990418032132 201 +1123962659471997957 205 +3056549312838577156 210 +1025661670765243906 214 +5397331336358247944 216 +7810366437124875782 224 +1195318972358038531 230 +7079722807026103811 233 +2524512050942986248 236 +1208593608912656389 244 +458260789232344578 249 +13194777122325112327 251 +5922704468287492 258 +11746235869336195079 262 +8611574268876189188 269 +7889840228953421829 273 +16998721522558936068 278 +6703563424903621638 282 +8885848295085850114 288 +13776273837475230211 290 +6036043703810622467 293 +2006225773287659526 296 +14202467530861800964 302 +7157057020317447684 306 +16885485872491802629 310 +12800303798361952772 315 +621325108927868418 319 +16727475898656483841 321 +6890112792805515778 322 +2421332377941126151 324 +16243404411124196356 331 +179400401794890244 335 +2630159406474274819 339 +1306609735592145925 342 +14908020842914311174 347 +1684452927247835651 353 +9400495923215416322 356 +8041860727239247878 358 +5619270496913133574 364 +2985476283152588291 370 +18150632792370312198 373 +13075355875451793410 379 +7596576612263365635 381 +7174955249282660868 384 +2272878747426984963 388 +9645618748109430277 391 +5995177571885476868 396 +16871713338758691845 400 +11801224416933808644 405 +15551192014010130949 409 +8196030292452405250 414 +4794784530053649411 416 +68047322062825475 419 +10163451915097363972 422 +4366630365820669955 426 +9174613115382159879 429 +17673253091692480002 436 +10710744348807818249 438 +6301209632168211460 447 +6557199531177304066 451 +10370980735304160259 453 +2426040420413965827 456 +18123352379522220547 459 +15891150425892429319 462 +16507447417454265351 469 +487708338428237827 476 +14107089365716616196 479 +747857609528251395 483 +17357876987202521607 486 +321005419951863300 493 +703083947315053061 497 +0 502 +17149635587492691460 502 +8277651075246678020 506 +1819886593879462403 510 +13106328552418381315 513 +17519686381941948418 516 +10696099526822671877 518 +4627984173327437314 523 +2628632462897246722 525 +3686397216490033667 527 +6617920799692924934 530 +6679301623707790339 536 +2596030458845084674 539 +13288938917088308226 541 +8348492885671808517 543 +6252009608718840325 548 +5807005916268695559 553 +15382799971167504899 560 +14954638692016032262 563 +8963684459383523331 569 +2934745887866391556 572 +8236887590303639044 576 +2016330563068923911 580 +12976290063611676164 587 +9986513189506445831 591 +780378482699725318 598 +383862355994530823 604 +7511344867307093508 611 +1435616864863593988 615 +12590979271693393411 619 +859813995721111047 622 +17910873098448224770 629 +16703366890805911553 631 +6922480979814889987 632 +8200210214462711297 635 +18382541080931060232 636 +12959023536126992897 644 +11055794376142651906 645 +8668012051305565187 647 +6795201209679524868 650 +3864186432644490244 654 +4574634299775772674 658 +2086703290536303619 660 +7145543127561014787 663 +9889572542971630085 666 +3510566585561691650 671 +10482036181312531460 673 +4296479271603189251 677 +17165580381790665732 680 +17931697598514948104 684 +5072138329769649158 692 +17857316349005986308 698 +1196313437880152072 702 +16094827446472526340 710 +6365083142954013701 714 +17639674970007880709 719 +1336948026798963208 724 +15719079816546418177 732 +453771991153695748 733 +15666021623592344581 737 +3887496731301423107 742 +16351565489992748547 745 +12913808626051103749 748 +9427161342471792643 753 +14610089064185748483 756 +11909740995340709890 759 +3386059367942955011 761 +7100313088634791944 764 +14954362273735097348 772 +5300343188950335490 776 +3306636399811602435 778 +15049176780536452612 781 +11478464585367391747 785 +4192691696663825924 788 +1724981527538165256 792 +8923121468991320579 800 +10407927314751914499 803 +4140577061391662082 806 +11024499228689010181 808 +11103397578962422789 813 +16103730809841527300 818 +2161511371026989571 822 +16905537098408481288 825 +14418359835235787780 833 +8643099440826274820 837 +15803230958149170691 841 +2270949347024239618 844 +16607521085023703556 846 +12520505897845165062 850 +10502193626894192132 856 +12350321094518214659 860 +4950437143309872131 863 +938542234576037889 866 +9547302901107668484 867 +7827404372121768966 871 +17757593377946824198 877 +13699186867246955524 883 +9859653826627356163 887 +16394835100035514883 890 +13800374264730731525 893 +16954635983094506500 898 +8015308433863798275 902 +858715644299290630 905 +4519655150699331077 911 +7134867591233050115 916 +6432786657037144579 919 +0 922 +9408341322832972291 922 +13653279902433200130 925 +1249019122170091524 927 +5444522055126761479 931 +18233734556082323457 938 +1838285473517654531 939 +10799019207790220804 942 +2448710159565130755 946 +18425837006146807297 949 +1384258267102048263 950 +6553795393861204486 957 +5022631533298058243 963 +2595435540421003780 966 +18298501952506793480 970 +17380720526409169413 978 +10291550905275666437 983 +8968303908578660869 988 +7762552109517888009 993 +12993351549860134403 1002 +13098482377540869636 1005 +17174134275815044100 1009 +2405939573849534980 1013 +11051603729345690626 1017 +2765842466801084934 1019 +13348255112383532037 1025 +4560899789258637829 1030 +17071422935680193539 1035 +11513452937230732294 1038 +1637355496640499203 1044 +14940739688966611972 1047 +8286559267538602502 1051 +6029036263825492484 1057 +6337648087046756355 1061 +12327119652833755139 1064 +7489768843341343236 1067 +17101806024406781955 1071 +1494687508867621385 1074 +915975103999953922 1083 +14731060910946571783 1085 +7993361195780195330 1092 +13688799604315935236 1094 +7328858946338903047 1098 +2913637027195678723 1105 +18189363439163655681 1108 +11261484070936291332 1109 +1244962005334571010 1113 +12618388435910808066 1115 +655187203027088898 1117 +1699259352638115337 1119 +9837815037477742085 1128 +10558465000768489987 1133 +3128326958710492164 1136 +16210393874387209731 1140 +3831602806328386054 1143 +1858477608543888899 1149 +11203849268139405826 1152 +14876215834473532933 1154 +838167957834962945 1159 +4472540425609859076 1160 +11410947109444917250 1164 +8435818218907397633 1166 +11045000766266457089 1167 +12325335880954441220 1168 +16708265953266297345 1172 +18342265362969646594 1173 +6953158344648897539 1175 +9922701673105435137 1178 +10113283973443524101 1179 +11668798096262926343 1184 +2129351334726026241 1191 +5692959118811792390 1192 +2917574127780044290 1198 +0 1200 +14420924818562740228 1200 +6098057863303978497 1204 +1252966646111680002 1205 +7111078464697947144 1207 +14144456899593720327 1215 +7367692118573781509 1222 +9319588592876439043 1227 +5212294342286609410 1230 +1600499660866511361 1232 +17579747388547180552 1233 +8365608306992954885 1241 +10307394306592963076 1246 +17092600292669807621 1250 +17030981925892977667 1255 +6929843536411176451 1258 +9908722951841282057 1261 +14685407131320535554 1270 +12861962652898171396 1272 +11958437143660911107 1276 +15904867421058229764 1279 +7283769647955500035 1283 +7872121678898447876 1286 +11726527760261815816 1290 +2316085662456682505 1298 +12840093831481137155 1307 +15574983692566917639 1310 +15176154862895929860 1317 +16186650646772958214 1321 +1965140296142659588 1327 +17362020270091437575 1331 +26356620300320263 1338 +4688323194808506371 1345 +470137109846916612 1348 +785647648524588041 1352 +686083037273571331 1361 +8705676087000994307 1364 +15985311040931325446 1367 +8848102120172622345 1373 +14900059783221505542 1382 +11611185676221023751 1388 +5823293000835959809 1395 +11173877492782561286 1396 +5985141512875075076 1402 +16607272189142469634 1406 +7000924871247012354 1408 +12796508861938638339 1410 +16352304696891085315 1413 +12654027566339262469 1416 +17652126895193709571 1421 +2059554016646703617 1424 +8824828815238545922 1425 +8026041213654553606 1427 +189105210507091461 1433 +8038465995762949635 1438 +0 1441 +4346653818095449092 1441 +13441396742193060358 1445 +5067771148519478785 1451 +210369551309682178 1452 +7856429334361659909 1454 +6456628847560069634 1459 +4777640967745320451 1461 +8983636279512822276 1464 +14568805960710332932 1468 +13817574021643753989 1472 +14625711259902278149 1477 +4632056779689710085 1482 +17613320542667293189 1487 +3172012402848437254 1492 +8040798394603101188 1498 +14064841209998140419 1502 +1914908168343121410 1505 +7368139610144548354 1507 +12868473585497306119 1509 +0 1516 +1618708134596732930 1516 +12587973098332420105 1518 +4964388169698209795 1527 +11644359715676310021 1530 +2644060095775605251 1535 +6430078223195648003 1538 +10183198452214045187 1541 +1240799682393062914 1544 +594310634075621378 1546 +2369514519273954820 1548 +10180653661786314245 1552 +954303650251543043 1557 +14430712698160791045 1560 +7362398115224322564 1565 +17170839233019868678 1569 +4334478792852912645 1575 +6976600872204725253 1580 +2757627166710815234 1585 +11581525848542896643 1587 +1902097979216049156 1590 +7092174838851165700 1594 +3776232881097953287 1598 +4956341896516184071 1605 +16560365104979398147 1612 +9985649880040289799 1615 +8870322153106933763 1622 +6905121755133908995 1625 +13368640352340902916 1628 +6681848478588709895 1632 +1825204937600832520 1639 +10492979809894170628 1647 +16021790814379410438 1651 +2537982728896871938 1657 +17110141827238231043 1659 +8972517116882764291 1662 +6878463938568223238 1665 +3653948979877717506 1671 +11414481194651397126 1673 +14522267179648162819 1679 +3098339502618796035 1682 +7079749050994126342 1685 +13571764215085394946 1691 +4748948606525397506 1693 +1577643399485818884 1695 +4080235243237779462 1699 +10874175738252140040 1705 +8407257242091918850 1713 +13208300770644489219 1715 +692428139842995202 1718 +1811883090719733762 1720 +9059362818280152070 1722 +1942856588307002885 1728 +8118332366482353665 1733 +4958069245857057284 1734 +14647311378680886789 1738 +10762024033896625670 1743 +28898254948429830 1749 +9834906317233815042 1755 +14985989359682912259 1757 +1282980713864208388 1760 +6063131598875265027 1764 +11171681444901584901 1767 +9942643440891227650 1772 +7536761905759707139 1774 +17586310513048226310 1777 +5368266791748388869 1783 +14231943828217691651 1788 +12518647321260815877 1791 +129394441281844743 1796 +2483490487411335170 1803 +654244401428041732 1805 +15646533714849457160 1809 +11807354932867949571 1817 +15902831808268765699 1820 +16275101253541722114 1823 +7489443708629377026 1825 +15395914353243975682 1827 +5617555619731661829 1829 +3134100206450675206 1834 +11607495136261988868 1840 +4974806308616426501 1844 +17446584074836170241 1849 +15686830167444742663 1850 +9706307518401206273 1857 +1668062460313515521 1858 +1175330870409010693 1859 +6316020408117881860 1864 +3926008952689808899 1868 +7412001888157663237 1871 +16350342416828571139 1876 +17722048717800707588 1879 +6638262866276511751 1883 +7428951476729761793 1890 +17816197047883941382 1891 +1346568064340942337 1897 +3701787015222295555 1898 +6659812133237486083 1901 +1828541539854978054 1904 +12379063259192634885 1910 +2611769333840765443 1915 +9618163593004828678 1918 +10135224491789939206 1924 +12979651712861326853 1930 +8882180359699969027 1935 +8839565787481092102 1938 +13328456084920556038 1944 +14232512278042323458 1950 +1868952656876792325 1952 +7567044498348088836 1957 +9878469525845452294 1961 +10877666723773861891 1967 +4437849393189355524 1970 +542122243470857732 1974 +4059190346138068994 1978 +14321675947144358916 1980 +14971180244834539009 1984 +7944574903635664900 1985 +6982417546170903047 1989 +9205813465909939715 1996 +14237044737088801799 1999 +636814072910696963 2006 +12520841226045264391 2009 +8898943418672995331 2016 +15646690259358356484 2019 +15618851112604340228 2023 +10285088843216830977 2027 +18286036510192394760 2028 +6450286360774949890 2036 +12025307250191760899 2038 +7044602746592181249 2041 +8270361223031661060 2042 +7199149542695273990 2046 +16798091800673956358 2052 +5285433079037354499 2058 +8498140496880657410 2061 +18434636390635965953 2063 +8780418579830073348 2064 +959965579978681347 2068 +2666650386212475906 2071 +4093783342266269185 2073 +7977153448080645638 2074 +3230317076849645570 2080 +2644129221999468547 2082 +7597431151331275265 2085 +6151418962808616963 2086 +16786361788616914434 2089 +9522044737514147334 2091 +15360350686533802498 2097 +4398995179394704386 2099 +4163122903470647302 2101 +18110267126768664070 2107 +17811600627481865731 2113 +11988559903619469315 2116 +5893679902922151940 2119 +3302430115655037445 2123 +2756050317441962502 2128 +7373324598575981572 2134 +15626353672087051269 2138 +9026268416534243843 2143 +5857105831257628164 2146 +11246462751297413124 2150 +7459631049065515526 2154 +2175352842263141379 2160 +9748465532031254533 2163 +12060676108130005507 2168 +8160425232164846593 2171 +1665947540125783558 2172 +10758171140537368580 2178 +5744770555727548418 2182 +15867521551313803780 2184 +11178209498970826244 2188 +2663862265833334277 2192 +646145646253570050 2197 +6886825228888300036 2199 +5219187155516171272 2203 +16142200027647465989 2211 +8727938199665870852 2216 +1200328579526163971 2220 +12449385538114001417 2223 +14632283715533800450 2232 +5295800027246062086 2234 +8827019094633400323 2240 +14543826221768176641 2243 +12388128316821831686 2244 +3087048392675298821 2250 +17669786912563615747 2255 +3879520399747123716 2258 +15648071975541157893 2262 +5580473107362200071 2267 +6895786389712974853 2274 +17709709086906012676 2279 +9627483233657542665 2283 +9602326803985618949 2292 +6748599026443758086 2297 +11488364339401397254 2303 +6716511183525677573 2309 +16003763240189186563 2314 +6003803301075291138 2317 +15800367754014516746 2319 +2817341800198731782 2329 +2110085916033252869 2335 +10353852055773781511 2340 +8745468498457416193 2347 +15197463976907486213 2348 +11844773108515011075 2353 +10745169896165544965 2356 +9502565595236673539 2361 +18340734722524717062 2364 +0 2370 +4877506240735029250 2370 +6632868101528461318 2372 +1094192348264738308 2378 +15930308455756352518 2382 +7517061312773919237 2388 +11537382714050522116 2393 +15343851421525887493 2397 +15685583084244037124 2402 +11443729733346354693 2406 +18096845502703148037 2411 +13060060807344890377 2416 +8226818503915081731 2425 +5171144332412330499 2428 +5367144440061049859 2431 +4687503341676126209 2434 +8115677569098133507 2435 +8753274682505368066 2438 +6767268893840927749 2440 +10747160183142327300 2445 +5318831768157948930 2449 +16744837601970291208 2451 +3968740997769839108 2459 +1041860322726726147 2463 +13185494599343868419 2466 +3781663100474830852 2469 +8664347289501861378 2473 +7145447006642560001 2475 +977858689003972101 2476 +188865761021926916 2481 +14781205616979726850 2485 +7514076159997088261 2487 +15227633270557658627 2492 +7486357174119883778 2495 +7899052859637422087 2497 +4312982947448530435 2504 +2484418012864310785 2507 +8450324929602980870 2508 +11374778755239228418 2514 +10780034123560756745 2516 +10313953391808102916 2525 +13836623279669341188 2529 +16297706918062760459 2533 +6404560275247226885 2544 +8323769790774729734 2549 +10061687257419431941 2555 +6724033317759518212 2560 +12265972209834273288 2564 +4748706107567735299 2572 +17588235414846031363 2575 +16029681841978911746 2578 +333014962274056196 2580 +2819861156000228870 2584 +17301319418358929926 2590 +14323022738651812355 2596 +17758251407482208260 2599 +9992216596142364674 2603 +5541911712511293955 2605 +1880849355295036931 2608 +15421034026101803523 2611 +2288503501826235907 2614 +2336333131728265731 2617 +15127408664422292997 2620 +6756061181968708102 2625 +2316367058427453443 2631 +13786932856453332482 2634 +17564157627292750852 2636 +5809790665868502019 2640 +9389430036410766853 2643 +15157257604368261123 2648 +523412383725034497 2651 +5270886391729814021 2652 +8987256414287503365 2657 +2751897370690544643 2662 +47819066577966599 2665 +9543124453318907909 2672 +15186331456703232514 2677 +9731347057535958023 2679 +6234700495105510914 2686 +17720066604242729989 2688 +611878128332703234 2693 +6029104170087404549 2695 +14612606995632327172 2700 +7357792311987945475 2704 +6074856230289873410 2707 +13368808999886628358 2709 +5918378978107988995 2715 +15624776793824203778 2718 +4241055509726121476 2720 +12687432015779367427 2724 +4003272975122620932 2727 +17483676776191982087 2731 +2701605488646040584 2738 +7387630099939362308 2746 +16331822462747681798 2750 +2197183442359868933 2756 +17624623361194542087 2761 +1749450990014992388 2768 +2888206094896619010 2772 +12985412669390948353 2774 +9843120678422464515 2775 +15590458610270713859 2778 +5950622975418741251 2781 +17607672802725530117 2784 +1225097419526011394 2789 +3758572251524375044 2791 +5891371767718009858 2795 +6843754938996156419 2797 +13418347525088883204 2800 +2887280155684756490 2804 +7867196614872225796 2814 +10992396837241625094 2818 +15526482250456426497 2824 +7582254907030848515 2825 +14309589056601523716 2828 +2843794758628944386 2832 +10106627892829635078 2834 +11117505412117820418 2840 +17559521087909430786 2842 +18410508844162253834 2844 +7796754440171003912 2854 +1826091018065355268 2862 +5568124937607335426 2866 +9164033835486570503 2868 +7917102923116225537 2875 +10708221634884163076 2876 +966446973350329348 2880 +1882776320247897092 2884 +18137433528115911172 2888 +7577505208556149252 2892 +3902521102041700356 2896 +11942362790107158020 2900 +2328713611561709573 2904 +8376513561567004165 2909 +18415012889800110091 2914 +7983446382889179652 2925 +2304166271864391689 2929 +708759182721729026 2938 +10774631175750681603 2940 +2608247964063907842 2943 +7317603117343176707 2945 +12615180422705001477 2948 +17995452459822326275 2953 +12439250137675515394 2956 +9947610136498965509 2958 +10340600516380348420 2963 +10073894039732477444 2967 +15954561361998232578 2971 +6039226287079734788 2973 +12684813664097613833 2977 +8337524429261820932 2986 +0 2990 +5738139389410570757 2990 +0 2995 +163262518049440773 2995 +11390362112332120070 3000 +7666496378417453571 3006 +17188351170280199170 3009 +14157925477049500677 3011 +16535316221715341826 3016 +701193705161007105 3018 +15417977144980853763 3019 +9623949443365348357 3022 +16537640731048440324 3027 +9880057250380779521 3031 +10507448958568448514 3032 +9901540867816521219 3034 +10882434502571251716 3037 +15939490563935542790 3041 +3818155241101528578 3047 +10810785028031231493 3049 +17268925026504538113 3054 +6000103580025957894 3055 +14492044616225970179 3061 +8964295197943843335 3064 +13244227239481936387 3071 +2072267724499101186 3074 +735562179013069826 3076 +3271477415853879302 3078 +1150251700717751812 3084 +11835839830005115393 3088 +17028480913889055238 3089 +16864969398419772420 3095 +9646252156141336066 3099 +5589333819644110342 3101 +14729039479109188098 3107 +2256025994407046148 3109 +5630416426912279555 3113 +23611161351524356 3116 +16061932977440933889 3120 +7560058124185071106 3121 +8943767870065516551 3123 +17388385529962317834 3130 +11686727589179028995 3140 +2993671307613155843 3143 +7451626547139373061 3146 +12726375988952098305 3151 +0 3152 +1735273330892205060 3152 +2746028049042776065 3156 +17093562035495421445 3157 +7598703106262353411 3162 +17526920923827930631 3165 +0 3172 +18087597149122765317 3172 +11336730259137625602 3177 +9704022087244797957 3179 +14531181144788964866 3184 +5103530438547424773 3186 +7049971328222257156 3191 +2593832991454060548 3195 +2549992206172832771 3199 +2656864556911864322 3202 +3094347590740453380 3204 +0 3208 +10556974365044028932 3208 +12597146506913681926 3212 +18243354473097630721 3218 +4168646291002030084 3219 +8893226051755120644 3223 +7904367695210051587 3227 +17247367703075879942 3230 +1338287165638264836 3236 +6734394253777139715 3240 +14645087877274778627 3243 +1841749727013933062 3246 +0 3252 +9793622484838288388 3252 +15384076833580083718 3256 +14678310837729104389 3262 +8947895455599830021 3267 +12421729442783160325 3272 +14382812703434878978 3277 +3484468606955360259 3279 +2411175954345499653 3282 +18322361710054416389 3287 +8989744845956541448 3292 +9637438279185886726 3300 +8282725403817063939 3306 +10727259769060221446 3309 +280860399088910340 3315 +3074647116268871172 3319 +9311932047626983431 3323 +2990333995786696707 3330 +11415454184475025922 3333 +8194042667332418565 3335 +11269986522125913093 3340 +10773634478079810565 3345 +0 3350 +4302235270674672643 3350 +4579270605621971460 3353 +3687011949425630213 3357 +9678333478858482691 3362 +14661606109051090440 3365 +9504123850532876291 3373 +14299233528797568008 3376 +10370491504729965060 3384 +286239823911254530 3388 +7969121812144744451 3390 +16606218867148559880 3393 +11756345184017143302 3401 +8204961944753809412 3407 +12456910480062157316 3411 +7569786299014196739 3415 +3372309516929818119 3418 +16631131943564946948 3425 +4436969913528429575 3429 +14467771002258720772 3436 +15278270405312088583 3440 +6638334178561090565 3447 +8154814430089498114 3452 +17289464348431017987 3454 +13185969354886446085 3457 +4725380864147687429 3462 +14933071000620043778 3467 +12471883028204926466 3469 +13286302152236950530 3471 +12020003522260348419 3473 +11784545509165047810 3476 +10311182359550097412 3478 +2262872037167824902 3482 +15672162207595698690 3488 +8479660175647360516 3490 +543122224331105283 3494 +8738610060644560897 3497 +15969479020845567490 3498 +3500 +5303047073946667464 +210658854139 +493093586 +15289397349632312454 +5941764183477191834 +3477193953305167424 +236453760381 +7470284155521404014 +24445261 +16426766960960540026 +14549236 +817365937 +1873618471841499416 +71893492 +10694515171064744788 +29330183088506125 +61997475 +4653200 +109445719 +8926052536804313893 +7528330190111771360 +1418462186 +5887104182899575287 +2625321597997091447 +23407864425745813 +1647838213 +6152225753094686522 +14151987057237756511 +18058417591402760409 +538510099 +17855463731522440261 +240752528220 +27920040887059601 +11078361536363433136 +12517601 +15885957841278600403 +518718202 +805438326 +2621553 +1550910461 +2411070513 +59965836 +13012951802392676509 +97518103 +2625321602295859611 +30277976 +546374457 +16759426304739641933 +259654328 +27356063970624739 +1873618458944931675 +6209987959894902621 +5728764444739437994 +18413109988782047308 +13885455448020813663 +13464164481390611573 +5514354709969504081 +6364097374632348674 +2676033351739376985 +1136798196293306910 +5299098874403555921 +2120987217453057458 +17306856587979066781 +1873618532028844481 +5572365145471912335 +18412263926676652075 +105382480 +5303047039553965447 +9881712940254169714 +152830562 +8610102806501591788 +15524263781940136850 +14282671233461718187 +2857298572705729021 +29330122900898936 +10554335258691243263 +8453377129057749572 +18411417864571256842 +811271050 +1873618489038604579 +4657106642463886071 +2676033356038145381 +514654951 +10757572347027851837 +4237766514325588729 +571999061 +9821766011288487605 +7230168968130792223 +2704904949959166469 +1823671323 +103350839 +46006654 +2755882956846859930 +15289397371128186695 +12662636664722033563 +16318735 +18411417894664929297 +5462796894122411284 +9950019064427710530 +6981729909914862956 +1992588707391932346 +63766972 +6422699 +23407808536904833 +15394822466617412826 +16881139139804531782 +14312300901618944289 +2625321593698061230 +9870724570679212 +5780604289886653255 +3870997034531752803 +2531021389865944442 +10908568553618343357 +1860700038481053299 +196215461 +1801847830 +24183115 +18424247431471827427 +14287090 +417019855960 +71631344 +4391052 +61735328 +18413674012989259870 +2625321597996829544 +17957750408840481687 +9870724568648556 +41943405 +2789363542978135882 +18412827950883864637 +548143940 +22151483 +17257283845880874759 +899112529018292807 +538247952 +69599701 +8510664359869943178 +27356081165698156 +27638084672359236 +12255453 +11400819049620310987 +1321272283 +16881139122607162703 +2359405 +3101815889301670444 +518456056 +9232147856523987724 +3758799212073651272 +3591160524196219107 +154600049 +17946608694533885076 +11500631658516907905 +825323275339564903 +9870724566615620 +39911783 +12318365723907459763 +546112310 +18412827980977537092 +536216330 +2676033351739114988 +11069796553860646809 +7880043043777809442 +451412296787 +18411981918872141859 +11678577273375754735 +8856014234050823647 +105120332 +1309344723 +162464400 +681145240220010584 +2626514825137096412 +6589396841525218018 +356832249381 +6156738032733324876 +11202456151687629452 +27638041680086900 +11243723090649876783 +5726358144768542273 +12498251711624252784 +13702827714901707594 +811008904 +8192198 +8714520725396523830 +514392806 +9960543895307946415 +15287141235608259625 +5727354401416546168 +1808894516123993997 +3686437022462641529 +5249797181178709209 +2625321589399030850 +103088691 +3062219857732765097 +830399540494469985 +530117487457144076 +12454108019635062383 +197984938 +8930986418384079868 +818873277 +16056587 +11526999220155450649 +6160551 +63504826 +7621890105505615217 +11847668763332905754 +10377426660276898779 +1873618519132015281 +18092519415945890646 +15882855708139391266 +7993599274919922706 +2789363538679106064 +2150364451440035988 +9870724570416301 +2625321593697799226 +91161094 +1410073577 +23920969 +7513578521803359945 +22279798815198594 +15520597512816297356 +1023125932615797552 +540017436 +8910392170935354895 +195953314 +644809585 +14024943 +71369196 +1873618476141774348 +816841645 +10906583479868327250 +1454041666728626384 +4128904 +18413392005184749654 +108921430 +468609401971 +16204201012116260706 +99025451 +9870724568385196 +18412545943079354421 +11878630053446878902 +18204249488608200784 +5566476545725367766 +17951898368652543383 +7558005371879033601 +16542141154387102177 +6316393479032998553 +11694336983993944146 +11427331956784106382 +4662073785906890031 +1873618454645640429 +537985804 +12999620585941961275 +2295119206548507606 +11993306 +1597536180772867045 +5299098844309358384 +8294669686619703163 +69337553 +1873618506235448739 +518193910 +5406444726343502428 +16765215479188031591 +5460499803636172954 +3431717683755289915 +28202117477106938 +5249797172580910311 +5745384143842643344 +14065038233622153931 +14311172801615955497 +16758489844492275047 +5510538272098551989 +11065487220741573048 +9870724566353399 +5679882735784101879 +259130038 +87097857 +3491703471172619422 +545850164 +18271599167641487963 +5991347923196709309 +1873618458944406678 +7033448275620070919 +812778389 +434977997061097911 +3445982126355516078 +2676033351738852867 +3545799512027105927 +1873618484739311861 +12749251354825264418 +14836382508930370955 +2625321585100000596 +21997756618246082 +8716776809328151764 +15580874176502892132 +3332575624131774585 +4445946672738010859 +5780604328577598853 +2848264744227112681 +1873618441749072804 +257098416 +4930631980557601532 +6877319166685482198 +1005889956380019628 +820642761 +17826079 +23125779236849772 +810746758 +7930050 +8929320279979198383 +9654763076979264499 +11949535972653271176 +1873618514832984063 +514130660 +18066207382028748450 +2573543666009114673 +18613585580197092 +1427238547443354327 +2625321589398768544 +102826544 +5903884228619468800 +4279043148 +7036226112429884975 +818611132 +15794439 +3324580943442478547 +1903640920853056624 +5898403 +1873618497637649718 +1133620887485417426 +10156853965084755435 +63242678 +282723005 +13586095437453200186 +9082058141968173941 +1987794462939089941 +13237708531286474753 +5240852582657493474 +1915314009235720841 +9870724570154139 +90898949 +17090754651615726815 +492307151 +195691169 +11050161621988804687 +23658823 +11623400942792738969 +9304480456320748248 +71107048 +816579498 +23971751058934778 +17869638717220195611 +1873618476141513316 +361675971417279818 +61211034 +1873618501936418049 +3866756 +567411536 +5302201063430292982 +8486888319115725460 +12406930521299355297 +9870724568123690 +11034422950646711803 +4287350254045103750 +5566476545725106758 +1923875870 +547619651 +6366353527348595732 +8597156797828894009 +13590665243542948895 +13237708561380147208 +4254959725487523541 +2907303882175415846 +1873618454645376983 +9230753948926543533 +11731158 +527827717 +5511666307614640107 +1330643932 +69075405 +28202091681942395 +4727296740454696303 +1992881785902860007 +18301216972081072101 +4076606659425995504 +9870724566091296 +39387493 +154075756 +5459976644113468289 +545588016 +12461042340477994821 +223556406340 +32432337723721245 +19595563 +2573543610120276856 +24535874149025753 +5196265237615086368 +17735566651085687884 +6204347601746593065 +1873618484739049815 +812516243 +6152225714402428442 +15291935501556190620 +15505670362359531298 +451411772583 +9484411285755463284 +161940107 +15292499508566297469 +563348302 +506004186 +11238431078799509026 +18323667541285735009 +2625321610894640833 +103179363763488430 +503001580666 +12769025487284210679 +17785259844527786731 +29612147900877606 +15290243377345399572 +17563932 +7667902 +3186488476490139978 +810484612 +1192315333980326167 +1873618514832721746 +15292499491370961900 +513868514 +5347351719937377689 +45220217 +11775490430040476325 +12240192446106372977 +35324256 +2396555433535145871 +7409502855497715015 +7888341864134085054 +4278781002 +1732546121802517809 +2374936041605498895 +21433680820701635 +12189960762281954023 +869984510486186619 +3598203394278688718 +6103488079777762245 +72876542 +16990917635978692369 +818348984 +15532291 +1146796961722731823 +17761874897365304540 +62980530 +4534407021717882867 +5636255 +32714379920409891 +12552846396214610071 +6262673798361580735 +2528483177756102046 +9870724569894177 +9297735470756268616 +5831598115918776853 +32432303331018178 +6064762127302393958 +6156455943246842659 +23396678 +13500652 +16916327697533962956 +70844900 +816317351 +18411699885273055253 +5884848047378859255 +5837238405281154301 +14311736903207619026 +5141736951422061236 +3604608 +31022281504523376 +3599049409094225259 +577045344 +2974323816123992770 +8021450341214588326 +3577503648415550265 +509805280 +9870724567861628 +11098517635487303139 +7462549834646555859 +98501157 +5779476207078475458 +219257375260 +490013379 +4222974949961697922 +6366353553143235674 +3158171969379764633 +21365044 +27638058876667848 +29330140097217635 +1873618454645114642 +2703776923039566000 +68813257 +279448782049 +814285726 +12237654319976351671 +517669620 +5779476284463187670 +10375505326587315831 +18411699915366727708 +6205475624366966000 +3307734082 +39125348 +1087507565178193378 +545325868 +15986098390340470919 +223556143025 +19177592590632702 +8865366478519731984 +19333416 +32432337723461001 +812254097 +11305519054433421356 +1873618484738787248 +5105416417023100899 +572982104 +505742040 +563086155 +104333894 +8070528080642443989 +11327137566841769230 +2625321610894378836 +16377260960560187819 +15586729198848181726 +1873618441748546884 +18413109971585663048 +4825924017323379312 +5915592292141435844 +5832726151436896491 +17247780946628644032 +810222466 +7405754 +11549275701007551889 +10161648502327149991 +570950482 +1873618514832459339 +313841222762 +4452458274095237609 +1445774942907271091 +6101795934071424788 +92406286 +5293539447540681024 +18331491793766525 +197198505 +11199980773228349986 +32432320526091507 +818086838 +1997667722089860216 +2524806027085153844 +1964966944 +15270143 +1370042529145686776 +5565348523104797810 +18331539082773742 +62718382 +2012415014 +18413110001679335503 +5374107 +14282027259104724924 +10375505339483621145 +9887461037680036022 +1873618544926132491 +4662355883991631380 +18412263939573940270 +157614716 +3295137431799204142 +9870724569630759 +491782859 +214958343888 +16875205763331852041 +7241607903360452069 +5408471212899110030 +23134531 +18411417877468545037 +27356081166681957 +644023149 +70582752 +816055205 +3342460 +5246976952665638015 +14212253575230457510 +576783198 +1842511416005692464 +806159226 +5566476498435574920 +15292217517958891614 +13516735047310051359 +5728764487730398405 +468608617008 +4025969582498383295 +16044698410490725659 +1519546451849645365 +9870724567599405 +5566476545724581156 +5619444426388998007 +98239009 +547095362 +27356033875641745 +219257112483 +8140646021471143544 +4713167439824750602 +16357059045845960667 +5462796881224795644 +9138963602338286574 +21102898 +10905173367761798655 +13701595356116683915 +2477484405147109478 +1880166538706292058 +11206864 +1283692271244348427 +68551110 +5885543833259674054 +18413673995792875610 +2352415791 +14947075702982868 +5299098870103476096 +681145240220994278 +163447447 +331038328206 +38863202 +96207382 +153551462 +2625321606595348609 +5461104757014004985 +10744889200825601240 +1988559907 +258343605 +6517011693716180143 +535167753 +2530175340657839273 +811991951 +15291935475760762248 +4397798264919820154 +18413674025886548065 +12109395139072755174 +475082778886408323 +104071746 +161415815 +8697110475982376165 +15584540329550678645 +13669583335851559254 +2625321610894116800 +1873618441748286746 +18412827963781152832 +819856323 +6209141854797957852 +1783548230307677653 +18411981901675757599 +637928298 +7143606 +15855332315905657597 +2625321864544389907 +12020808312486431384 +3076135121411313050 +10139438201185111279 +6152225744495577231 +33560368941368890 +210659313158 +4278256712 +27638024483702949 +24904017 +32432320525830439 +13263754581809432790 +817824692 +15007995 +359800716494834349 +18613516794268696 +9839328478246341893 +62456234 +5111959 +18411981931769430054 +16219982623696489082 +6261827792145090364 +7692717626264324682 +42664306 +13806855580317125108 +9870724569368358 +16269555352897260337 +214958081659 +11214563466575480865 +15636771529559117046 +13271165719268362246 +2652485274356286816 +538968856 +3784724792312663401 +18263821886743185772 +1986666427421953426 +5565348480114297669 +5352348827359053328 +12976359 +1873618476140725820 +421319345246 +70320604 +11703165067112811597 +21715697223994697 +3757107087862401328 +60424594 +3080312 +10697899350700788395 +1873618527730534170 +468608354196 +509280991 +50528646 +1193603335023233930 +16635669954819197974 +15426482629288462533 +5460499803637156023 +2625321602296318353 +9870724567336570 +97976862 +8818864638845060491 +14288223544298637564 +88080898 +6996745855548787140 +5566476571519223063 +546833214 +220421203678071202 +31022238513759415 +1873618458945389823 +6406389097441592980 +20840752 +813761433 +27356085465188671 +68288962 +5865888353649363875 +109394696450803010 +12213481117926952067 +18413391987988365394 +10944716 +517145329 +5723537903358642458 +21715753112570631 +7758478083289188556 +10675690836223986039 +153289315 +95945236 +11547019543992076059 +9649086479758069023 +2625321606595086582 +258081459 +544801575 +5887799994573980828 +2845029447323880298 +18809125 +8510103668314541335 +6205475701751155414 +1990332636357069057 +429916882098 +2673382969485886910 +1873618489039064439 +18413392018082037849 +10914208898869168291 +3773122177597967623 +161153669 +103809598 +14107087915135404740 +6366071515245381876 +18412545955976642616 +15289397371128645360 +5462796868327967227 +1402930148 +28202057290482949 +797695489810761887 +16777494 +18116142943679220675 +5142301044413893172 +17219576355390295334 +5249797112394286460 +13735950183222348532 +6881458 +29048192479791616 +16896582888638318388 +14517406836956661503 +5458848655886518922 +313840698753 +5197393273133271298 +3861350810962691992 +6375653898722412075 +16885380374869314205 +361129707266 +210659050964 +29048123694646491 +3017170418691476659 +1873618450347593089 +15290243360149277503 +14745847 +72090103 +14546784569801180959 +7431889721301470079 +6364097387529111599 +2435475427475262665 +1873618497636600365 +6151097734773868363 +62194086 +17083693200934636558 +32150372909516328 +4849811 +3172873313800750756 +2150364429944620611 +3862478902367620470 +9305858029919208637 +2625321597997287853 +2508194873 +491258567 +1408762855 +5015996636573993090 +2414921941537785811 +538706709 +5734260728554980678 +22610237 +12714212 +70058456 +6208295882974168451 +32714336929384395 +16643035121679272213 +20023641798084435 +4770547828131824981 +2818164 +1930668198955452820 +13726068529822894439 +468608091255 +5569296714050766113 +17490170188584258190 +8694008299851745161 +7073102484926630551 +155058804 +97714714 +40370537 +2625321602296056238 +1703347206 +15895039144349470066 +5352348805862656188 +3068049059797011246 +5880738612678821404 +12309852946450942075 +33560429128451329 +15289397384024950845 +4767727591019973374 +10682570 +10233718743719545342 +850088361543927300 +2792183694107936667 +1107456968073808590 +5759560470823897206 +162923155 +29612216687004362 +5875369269012203157 +95683088 +294416195335096411 +22279760122415532 +5639662680184522626 +17619012653768771484 +13237708544183762948 +8550520059753138843 +27356042474686002 +249849483538007723 +544539427 +13390152586296232130 +10906513561824594910 +18546980 +1873618489038801706 +2676033356038342054 +6313103561496791450 +2063139881 +6848542126596623056 +160891523 +103547450 +14101293042239958 +6151097653090126690 +1584595969 +12424382439595706534 +17698252132056434004 +4129856573689694799 +16885259953617962521 +12393440069873436875 +32432320527338097 +21433680821684597 +8617826180017097033 +1413046597527668667 +3973491001936446780 +819332033 +17305802226190387588 +1873618467542665344 +16515346 +6619310 +6206321690771522709 +4089771542585346905 +1223976962194278208 +13487493291780736605 +2487491354099451134 +8854886172739175692 +9870724570875039 +2625321593698257851 +1535116279 +6262673798362565305 +91619849 +493028049 +5352348797264856883 +8143564249694210398 +6151097683183797493 +9386257309953099582 +196412070 +3865299044899163405 +71827955 +18613366323088485 +18157949162008873831 +7562235583526800081 +817300400 +4618470194090937269 +4587663 +3932922014897081298 +61931938 +1873618497636337289 +2522831856378710008 +6364097413323754682 +6053028402293443390 +42140016 +12287601267178473523 +2625321597997025900 +538444562 +15991329612793777185 +15291089478142986477 +12452064 +2676033644081056812 +2556016 +16508579235574254010 +805372789 +59900299 +14787093348585572176 +2575517759332551933 +2412665810316625225 +7730749911729375728 +6155298010574883251 +10488220504998020326 +1311572948 +883931539946605906 +5352348805862394041 +2786543383251193103 +546308920 +3346269252 +5782296426993943791 +4469799173763958889 +6205475671656957491 +7872981661881076049 +18116424960081923281 +2676033351739311464 +516621038 +1465168459078698840 +5677488692584514734 +105316943 +4562124351240801677 +5245848874158263187 +16432982289349543214 +162661010 +3971798877726246151 +4787251587800828866 +5875369294806846690 +12217235256243064050 +95420943 +5354604868299326678 +4502324021619918399 +544277281 +5940918086979029952 +2014710471177341259 +2140013610 +1873618463243635741 +18284834 +2676033356038079832 +10531295876509927029 +5458848625792321791 +18411699898170343448 +7410231625909407077 +3478039985316562895 +6204347606046083061 +31586254122912349 +6829167320236755019 +27920101074341046 +13165236096819726043 +32432389312220424 +571933524 +5727354401416743090 +10225919154718574351 +4127600472563058730 +160629376 +103285302 +8483828720842049762 +15740334315622960494 +206359759935 +9813006656186419950 +9319686106503382840 +5515085278788979157 +232154663489 +26149204 +6208295848581203181 +3094190453106412515 +6520986101609793850 +32432320527074663 +5245848925746038203 +5942328186188203485 +1873618467542403595 +16253198 +15881445561639371975 +6357162 +63701435 +15515478115209971466 +5833854247140395797 +283181761 +19177532404009207 +16567374854657149772 +684134257893509654 +9870724570613070 +15680489859993767209 +12826571498698443033 +2625321593697995819 +10329316755526125416 +10754752208794748192 +10758418391935812957 +12105446909435186010 +3143159678306028631 +236453432350 +540214046 +14848239906707278405 +29330157293274228 +684134210602468610 +817038254 +4977791693940394179 +71565807 +1873618497636075077 +807142269 +61669791 +11287403619712895066 +4325515 +13819298136066198 +7734678113259293802 +6098975847429179176 +99222062 +18056758355458722638 +9870724568582655 +16224960573811657069 +2625321597996763849 +4078298757842341053 +17625510063045740642 +10528906628815718922 +490734276 +5412367062202975465 +22085946 +12751507524739009261 +538182415 +12189916 +18413109984482951243 +2541195915421354200 +6671860954713623381 +2893509029140760671 +69534164 +747829823970020707 +6770804071406897080 +2293868 +5566476498434524382 +6534429686359852912 +18412263922377556010 +164430493 +9870724566550039 +154534512 +10167299845199168903 +12754891682880490747 +5250413516934022944 +3315661715940248009 +451651625195343029 +32432333423379563 +5941764217869305943 +2141783083 +283748271730 +10161648493728303880 +5240846595623881868 +67502526 +15618641120352995308 +2676033351739049517 +6205475697451599682 +4023356732265137752 +14986955239351847842 +31304272112126853 +516358893 +2207492698791414354 +477207135345 +1309279186 +105054795 +17859691850682797212 +162398863 +4238330517036600601 +152502880 +18412263952471228465 +257295025 +10905173350565414454 +17498716255300421272 +8881019260503721949 +18022689 +534119176 +18411417890365833232 +6293435910568086045 +9374458755688828226 +820839372 +6153071780807051278 +5909364179964069981 +8126661 +3735453693364143828 +6155045908522469290 +745740842898098858 +2625321589398965240 +12142525752872799042 +160367231 +17958290734101235336 +9523554809025136564 +16892239439269464715 +15289397371127860096 +1736311827 +15991050 +63439289 +6095014 +12484855343804124176 +9658025172156550406 +18067928153034001057 +292345808939 +16572875051796793000 +10542598463376395267 +12772641161582545873 +18413674008690163805 +1544487931 +14737352740221028816 +282919615 +12808641794728789765 +2625321593697733840 +17128487303121020 +1706624008 +14101026494875963 +11214563466576463780 +18412827946584768572 +11966722661119888545 +6156455943247300775 +5300226909920168653 +6004915412369541960 +816776108 +4223816177647290930 +71303659 +1873618476141710425 +12477949191893683608 +417019528294 +9511403338599564690 +4063367 +61407645 +2543805385922512178 +9870724578216632 +5407707525201267705 +9870724568320021 +2564752444 +98959914 +15494005608834598990 +15140097999495498431 +21823800 +12734096628671909131 +537920267 +18412827976678441027 +11927769 +69272016 +18411981914573045794 +2571498445011814318 +10592171188278987146 +2057911839619745748 +9870724566287831 +154272366 +545784627 +17616192489740896443 +21715680027609308 +16886908734816455284 +583336804 +2246313005 +516096747 +2625321585099935141 +620888934 +162136717 +331037018572 +477206873177 +503001777494 +15592058013925444099 +1652810939277510396 +10531295803425490030 +3205882223899445065 +31304323701671300 +28484129580057898 +1873618441749006513 +16893851890367073119 +820577224 +16904712944498838074 +1394017249 +17760542 +4160689491693538063 +4047541379259827663 +7864513 +14219872676477209184 +504169174 +17244622751296785814 +2625321589398702921 +4278977611 +7239633818635733091 +5462796868326918190 +1334641629 +73073152 +7460569593843485201 +15287141188316891641 +818545595 +9339868219275806468 +15728902 +5382561551670903978 +9373330690077689939 +18413392000885653589 +5832866 +63177141 +438515402871 +2373415502940997016 +2148672322930150296 +168849237244054062 +12339564610979564477 +8327325764367420682 +7630443591734791098 +12608147700378373379 +9870724570088730 +2150364451439708714 +18412545938780258356 +13221120945827219803 +492241614 +4129856608083381232 +15740733274947783803 +15858116883009440274 +1873618476141446514 +816513961 +17564225130023161250 +13697261 +10668197763104573447 +71041511 +5357143003026951378 +31022281504720056 +1873618501936351339 +3801219 +442814170389 +5701610621477129021 +8520914754064026558 +15289397306641222853 +108593749 +98697768 +9870724568058057 +5780604294184830225 +156041850 +5192881006389626514 +32150304123324262 +219257572663 +18412545968873930811 +5249797099496672683 +11127945220196076778 +9103100569952650951 +11665621 +421318034537 +17619012718254098754 +14443179094226111164 +1873618480440216958 +69009868 +10594427319499622429 +814482337 +13968724050119231192 +28202091681875145 +27638110466671725 +16166203682344470241 +1712194570 +472907842721 +507970270 +15580874172203795679 +23689855033805297 +154010219 +17092164759424403479 +12893049762838873864 +6877309693745106245 +545522479 +5887800020369606783 +14977809576148535095 +19530026 +14105033451515939293 +6795216411027442152 +2543452128325209336 +1385890784 +114426460 +6444189713816225654 +6152225714402364510 +524384476410219715 +17953567922355439196 +17113993018971653874 +573178715 +515834601 +17090754617222956318 +161874570 +1538130937 +47186305 +30458188512103543 +2449021711964768402 +2414448843017751282 +5214737420442796133 +505938649 +2625321610894575340 +13965057806789381527 +970700105235760464 +15223822230290106035 +16285378285009240167 +16940455997476965252 +2601013084734032090 +5248157445900799208 +1580068669843704469 +15043322265989680207 +29048166685607288 +3863606942184311140 +820315079 +17045009756596405420 +29048192480512516 +11510172448171493799 +5885976160280708469 +7602365 +17785259896117529586 +8856014216854897981 +14477731067643038195 +1873618514832657292 +2578187325 +15292499491370895395 +33560368941827284 +13146357072728951328 +17353152791227993245 +159842942 +15530553734630409457 +5569296726948055802 +494159375523777824 +1812923415 +6366353518750729401 +4278715465 +17097308613030775025 +35258719 +1899651063193471062 +12103109825679658143 +6364338522051512284 +2429880031182916564 +11621189233770302317 +72811005 +15466754 +3880024017885400135 +818283447 +62914993 +4076606625033226775 +1873618497637320883 +7746405201714873917 +5570718 +10859426818132543221 +6925759835249836137 +3506237898852665380 +23407812836853915 +1873618523432225060 +17166316876055971050 +18008952305986046279 +43123062 +9870724569826462 +7410173966093388838 +33560399035500221 +511599051947 +214958540605 +13237708557081051143 +20587696099952690 +15339421027537585423 +6104586261132347910 +11103300151687644832 +1456931819 +1873618450346281005 +9181531069949872018 +14650572868605052119 +17783567759008991682 +575239712866634722 +15288269284022357372 +6206321673575138470 +644219759 +13435115 +399811749952817933 +145335345147610979 +70779363 +6366071455058494624 +7529998377695250462 +519635711 +3539071 +576979807 +9568723490388248888 +634323816 +13012951802393594980 +853643387796785445 +98435620 +28766107292140894 +9181555677596944971 +5195701200510977145 +5129024196560096606 +5831598124518278362 +4844858457232050089 +219257310372 +7569568047215545466 +5461104800004441485 +1518418407735101149 +814220189 +11403474 +18005251247539029895 +10333839787251271664 +1836516380 +8054758354584013306 +507708124 +163644058 +9001701177466488459 +2625321606595545096 +153748072 +4787251587801811388 +39059811 +545260331 +2036204584 +5356296971014964874 +19267879 +9714916684781063078 +3055188874828713383 +14576212124415364447 +2150364417046743283 +4662355849599126556 +1372824966366170355 +1318388695 +15289397293744393060 +8423108281783224429 +505676503 +104268357 +477206348880 +5831598081526006949 +4625631396377398109 +2625321610894313322 +6206321759557388696 +12237654281284815334 +17236251 +9391897711091583990 +3891732840317912522 +8856014216854636141 +5758903550139959418 +7340217 +638124907 +810156929 +6206321690772243584 +112132697 +15287987228927658628 +339636063086 +7721139320100816372 +684134305183500639 +22279768720672168 +5831598111619679502 +14814059355306855043 +4211213383 +15290243360149735302 +18411699880973959188 +15204606 +11507341268100646834 +62652845 +6365225483234117329 +5308570 +3491703531359374171 +17791918762976347730 +4127600455366674792 +11130039777759856047 +13951205954302381098 +18115578910873816258 +8659114857360722535 +6153353844499089111 +157549179 +9870724569564298 +16327183209838150989 +491717322 +214958278120 +32432303330691092 +17684252729367202593 +16965951797418331227 +23068994 +2272905061487347697 +1873618450346019367 +7515799761807542411 +815989668 +2576363817137867614 +70517215 +17763448248357489818 +13172970 +3276923 +806093689 +17621268802185464283 +60621205 +18411699911067631643 +576717661 +1685722535145180234 +23689824939607125 +17256155806064642777 +5516892801706297876 +12982659022915898414 +9870724567533791 +15515140725455259155 +547029825 +219257046468 +4180850416920431050 +21037361 +68485573 +11141327 +813958043 +189614828176542708 +1873618480439692390 +279448454880 +16253215886083360174 +572110149897422243 +9896616181508082455 +153485925 +8021450371307931626 +38797665 +19177566795402134 +27356016680241600 +669582195 +2625321606595283106 +554894151 +5512098557251945790 +9568883447315500158 +1440671446449589035 +4502324021620638916 +3249068390006196153 +15292781563660995825 +821822415 +27356063969248337 +18413109967286566983 +10911952793442192048 +6064503826171693679 +11161692095903435283 +1004761907965660269 +2207210695286917386 +6388664954993575829 +46662016 +5885976061401368013 +104006209 +5572809636517250553 +2625321610894051277 +17955470565775510239 +4661227814082512385 +6368045642960996241 +5463642874544129714 +16974104 +533070599 +809894783 +18413109997380239438 +7078069 +637862761 +6288511205539515238 +3974700764184054454 +18613559784442970 +2791055594105669609 +4504298205224635444 +18412263935274844205 +2605266760616185153 +15287987228927396675 +339635799228 +92078603 +8501910827968825512 +5991347884504386492 +210659247559 +17284241873202253123 +16893851873170950707 +651404368114879038 +18411417873169448972 +24838480 +5726226344404977639 +10259573046193883986 +2676958769323838072 +72286714 +6886936648282539655 +14942458 +521143041 +5046422 +13980703149896829784 +1495991284 +62390697 +18199185222634702635 +8834282535679560676 +15925946803693423456 +42598769 +9870724569302153 +5459976661309982295 +11084138473134491150 +5303047078245827995 +214958016090 +12451287838412704489 +5509410202188647833 +2681814701524780811 +10628953736434486617 +9774054990929462949 +18411417903263121427 +3865299049198390675 +12910822 +5356297009705911966 +2421359666 +70255067 +2248112069177510680 +3493395634074945822 +60359057 +12654580528992553525 +519111421 +3808100888100343209 +3014775 +13513632858283052077 +15289397310941235057 +8861613698626554738 +9697577994188492052 +155255415 +10381427610856195682 +9870724567271440 +2625321602296252770 +14512708438227029368 +97911325 +489423554 +4022831255438034250 +30671195 +1873618458945324208 +20775215 +5459976691403654584 +813695896 +12665415616966166285 +5645056620059298667 +68223425 +1319896024 +2390363305266056430 +17634738504986593825 +20305632407192782 +17462509665872383079 +1606616067 +305243098454 +163119765 +48431492 +10590197086357423689 +2787671431665157349 +6366353484357502971 +18413674021587452000 +17620986833073014515 +105775699 +20869665212206112 +4445946672738929841 +95879699 +2625321606595021110 +10906583445476542150 +18412827959482056767 +17205553309096938840 +12294570438877711433 +5461104782808583112 +544736038 +9950019055828534995 +5991347927496394467 +811664269 +5403008449516603011 +18411981897376661534 +572392279 +7677136701370927115 +6155045908523191668 +18067928196024961188 +20587511236070012 +103744061 +161088132 +335336768790 +6155045934318095559 +13322381941750499717 +15291371425760087333 +30740222110467489 +5245848925746498573 +5349308051975768286 +4548309565419816229 +255984301 +5461104787107351969 +16711957 +10906583475570214623 +6365225453139920066 +6177363118375897150 +6815921 +7032232753418799293 +5558136817694803400 +4030203865610717075 +12718336251608304605 +18411981927470333989 +1545208828 +15287141235606883137 +5837238474067478018 +11705421198335413148 +5524868651610213131 +210658985303 +6098975770044925746 +24576334 +13151687854617134836 +4662073803102881076 +72024566 +817497011 +29330157293733695 +17096567568145714575 +1454859013759438228 +14680310 +4784274 +62128549 +1493907215600323645 +6364097387529046615 +12583654612056476062 +12851509922494416016 +1495729137 +15287141218411547437 +828143439367899804 +2523959969279970191 +3919394969679695174 +7595953279435999504 +2625321597997222413 +491193030 +1839046019115124804 +7241043922144659849 +18613499598604650 +18413391983689269329 +10594427319500605883 +12648675 +4861149623842704773 +5782296448490276391 +5516046782590617836 +518849275 +10015828607276288922 +15662612681012938353 +2752627 +60096910 +5133829485924779401 +7003516464553396964 +12903069678853164419 +2625321602295990612 +97649177 +259785401 +5464488953846367762 +546505531 +30409049 +374027977988 +1396769762 +21715680028329254 +5637072609524124450 +7731877951544692100 +1873618458945062288 +6767393152337644543 +9467310877347154547 +5429433323061448040 +10617033 +1730937871 +107356700000258304 +425617786716 +451412690018 +18413392013782941784 +12020684574736647824 +105513554 +3541851256594893702 +16038494049631274933 +497025749 +4661227783988316231 +18412545951677546551 +5565348467217401524 +14428481252717692252 +544473890 +3344434243 +2169005683868174908 +5993603989931887912 +12972952285742288 +13117263636444153530 +811402123 +2676033356038276482 +1873618514833639109 +514786024 +572130134 +160825986 +1938490399 +10280579133800254203 +285938493736356261 +6425213859614951480 +103481913 +11364576519499679975 +1881294612915292853 +15739206202722094240 +4397798316509039896 +17011915733784398286 +1873618446048496233 +14383326641327005 +26345813 +6156455960443095577 +14975681650483333306 +819266496 +16449809 +15288269301218674108 +1873618493337504776 +5782296461386581535 +12162857194684744950 +16633695839999756254 +6553773 +6206321690771457172 +5411573444917201071 +14273081993166850387 +17297538988880889355 +9870724570810095 +339635275824 +101450287 +2625321593698192308 +91554312 +3812049113439014303 +492962512 +15289397349632182266 +342928503145892901 +9257009393629660721 +13674941621707869313 +17952462371364276975 +24314188 +7676326001635166459 +12622921449567619867 +14471968401314024391 +14418163 +71762418 +4522126 +1873618497636273356 +1873618523431177265 +31304285008889193 +2625321597996960522 +42074479 +18895601982637667 +14883032307819284131 +32178524 +490930885 +5459976661309458015 +194314911 +1873618454646032908 +9386257314251803173 +13950077918785243724 +5831598146013367591 +5882159627828332650 +69730775 +6100103913039400051 +15744000533156660854 +12386527 +518587129 +59834762 +9231865831523354279 +2490479 +2148672331528407961 +2908260051937332390 +16876615841046071902 +9950583114428779661 +154731123 +13237708539884666883 +30458205708158447 +2964529530791004471 +40042856 +2933734509745341832 +5459976691403131036 +1730675726 +1873618484739705502 +2676033351739245930 +15215179494928287321 +14866462842593414402 +5463642917535614049 +631243623 +5885261859847867262 +11391362031143292020 +506659547 +105251406 +5778348197355914873 +16324853745603185849 +5509410163496651347 +152699489 +15292499534361856724 +496763604 +544211744 +4078298792234977417 +5461104782808057591 +14648423506775771515 +10504814416598927327 +8709732826087622782 +2544766567488424310 +811139977 +17088205463377873568 +15798241638577276499 +2676033356038014277 +2785415326238639918 +12562453432512743836 +12350988444867431112 +1873618514833377412 +16940553195690134509 +45875581 +103219765 +8854886168440079511 +5941764153383128192 +2625321589399162008 +11818157132458100908 +2785415278947600352 +15257764832492062794 +232154598652 +819004351 +16187661 +4644563108626631009 +4000515045253449269 +16872667624306444468 +1873618493337242815 +6291625 +6156737968247080128 +292346005443 +283116224 +3220426554520570467 +12356593998396393868 +684134257893444250 +17175427809786595961 +9870724570547380 +1992881803100621054 +2625321593697930351 +9450798976826149302 +16655465042802838677 +6474545510181176536 +11740202404159819072 +15289397349631921063 +9714916620293637762 +6098975770044401989 +16364556117061994922 +196084388 +540148509 +24052042 +11065179658016983681 +12480382642832672298 +71500270 +7285785859232107205 +14156017 +17632571483632043275 +61604254 +4259978 +17750109864738752812 +1873618523430913566 +9830100417878166271 +14425661002709010016 +4794173760728861833 +464308734399 +510460641 +2507605048 +41812332 +2679637056 +99156525 +16044698410491643447 +9870724568517151 +5516046735301085409 +6261263733545503259 +3759645248384009814 +538116878 +5779476232874035736 +6104586261131037638 +10531295842117158093 +12124379 +69468627 +5565348505908348542 +814941090 +5299098870104394759 +14322284629040564382 +10440328872292254866 +2228331 +518324983 +16872385650894636566 +6284197438710222140 +8098722631875955846 +5727354392818878727 +9870724566484489 +154468975 +2292825785040636736 +3172873343893834792 +14418466534433295118 +2707725182771857350 +15293345523383077603 +259261111 +19988781 +15371922320578972378 +19741625396299098 +18411699893871247383 +12818875419963886521 +2676033351738984017 +14268291611706526293 +1309213649 +104989258 +6367324841362000185 +7432602967203907143 +11331649863678691999 +15292499534361593441 +1815413785 +5778348223150556659 +5572809636518234139 +11408348231855703653 +2446197814 +13001682102565734253 +17186370630874106258 +2785415274648570354 +14264783202905229777 +7171706723174648069 +820773835 +4645667113710455153 +16425638839461284611 +5353476806987745228 +1840738151924108521 +6153071806601889790 +810877831 +8061124 +5356297048398365877 +4770547841029572913 +12804866717273491655 +15580874133512784221 +514261733 +571605843 +12346762090311779845 +102957618 +10907429529076434052 +2625321589398899121 +5354604872597767596 +4279174221 +27638024484621167 +8483828720841721486 +1459422188 +23689889426704296 +17648172271756969893 +232154335723 +15925513 +10811668319096800853 +6365225478934037607 +9763237054719266042 +11633356565151157114 +63373752 +1873618493336979326 +6029477 +3580814869236944221 +5199085482290645376 +282854078 +2625321593697668091 +9870724570285675 +7449919019336600171 +1839046014815569788 +23789896 +9131616131521448314 +5779476228575003910 +5511666277521099409 +13940760354079114484 +18413109980183855178 +644678512 +71238122 +417019463453 +15131353489256221185 +447360420122266222 +520094464 +3997830 +15096032016463431129 +1873618501936549084 +61342108 +1873618523430651633 +18412263918078459945 +5344573059048999857 +5155859771100236117 +5405598659939206416 +27356033876298083 +2146416200305806198 +5303893093062347743 +21758263 +3189961199463959445 +527958790 +69206479 +11862232 +6364097396127827248 +1320879066 +365262179507571896 +23689855034002659 +1473119215 +18412263948172132400 +31243224015702806 +39518566 +9870724566222277 +545719090 +5301355009924597043 +9391897706793274792 +11514789185312918199 +18411417886066737167 +5299098848607995194 +2284412389694637269 +10530167802300925091 +10427987387505837891 +14322803714593785119 +2625321585099869531 +6829167367527204602 +6013889919468112625 +4181978486829943864 +8698802578697685482 +1654120425802828663 +5569296748444387676 +1873618441748940565 +256967343 +5245848947241584851 +15862817677379702068 +14633483086300318059 +288046714075 +2203332276215481610 +7798976 +810615685 +237175467 +11340219378265033230 +313841615983 +513999587 +18413674004391067740 +2116750858326574509 +8070938101082033295 +2625321589398637514 +25099937047839912 +5245848878456439955 +12118995007347033900 +4562124381333884039 +31586327206235137 +16436648502583690678 +9181481831755875838 +5516046752497929091 +4183106466458307862 +1991460714865167155 +17082847207615301902 +818480058 +15663365 +73007615 +3701600990787603378 +63111604 +5767329 +579208034 +1493907215601306869 +11535686880442518166 +3313969578832561394 +2704904932763174902 +6570315963541227654 +282591932 +5726226297114658480 +17160329975787685834 +8843457619279611284 +18413674034484740195 +9870724570023121 +492176077 +30740204914083091 +21433663625497129 +1629160452 +1873618450346477252 +18412827972379344962 +5243108696682924272 +7260902865540482639 +816448424 +70975974 +15287423196122254433 +1873618501936285414 +5151629580948802356 +3735682 +61079961 +18411981910273949729 +7837634943338155161 +3597357340772992368 +5133829485925763690 +51184007 +10956724774926813288 +98632231 +17309267256018536307 +9870724567992379 +29048106498198701 +3544107379218385465 +14386655907412249373 +219257507157 +21496117 +68944331 +16330874579771459902 +11600084 +11124082762859154482 +5459935770830768809 +814416800 +347984565637089693 +11923578915473263059 +575144796 +517800693 +3297856681506178941 +326737923180 +16038494049632258844 +15104099179857577674 +32996413518841137 +153944682 +2152780467316001469 +8722536002903082945 +10646954815923686447 +545456942 +14458654042895551171 +3935742187522887052 +16064731596255856452 +19464489 +17648172288953812474 +6213874949885069218 +14851060135220743194 +6471725260172231870 +4504298175131421894 +573113178 +11701191021079496730 +12314601354656483126 +13957562954616997312 +161809033 +563217229 +104464968 +1366033375 +1133620930477295468 +6209141923583494372 +2625321610894509848 +5052785364214352114 +6155298040667702671 +5246977012853376412 +4074350485214726972 +27328854 +1873618441748677997 +2000487899013646903 +7465404271946632160 +7239351853821397993 +11742834345080916462 +6368045642961454306 +5516046795487905107 +434216307724 +3493677603186412637 +810353539 +16633695840000739887 +821147663836514852 +18413391996586557524 +7536828 +4151361015346562251 +14540810596246030644 +5995296139937712949 +159777405 +8816997369364548341 +45089144 +18412545934481162291 +9298403582666148514 +15108492788614827244 +35193182 +5568582435113995179 +5570988833963444820 +15289397375428069113 +15401217 +8430474765433179073 +10750398672578676906 +72745468 +5405598728725859379 +9250794030848869727 +62849456 +17422075106091075868 +5505181 +1873618497637255436 +578945889 +13106160036035691955 +282329787 +5570988786672405753 +9870724569761068 +7031431794891230329 +43057525 +1706034183 +491913932 +214958474959 +90505732 +18412545964574834746 +32432303330887118 +846140170598090257 +5458848587099997499 +17607182983838566334 +195297952 +539362075 +5460499872422693597 +23265605 +943759021439519007 +70713826 +816186278 +2207492642904016905 +644154222 +60817815 +806290300 +3473534 +1873618501936022824 +13307798926833551183 +1873618527730926929 +11349795265056081195 +567018319 +9388513449772451585 +165610142 +2625321576501808484 +7290339324003420579 +15287141244205140113 +41025899 +9870724567730368 +5569296739846327213 +98370083 +1531970550 +219257244681 +2065251783916127931 +6151097665987347595 +1407386597 +3973490993339565383 +12463417266756127924 +17631161371525515669 +21233971 +3232498753 +4767727591020628301 +8972557000702888938 +1873618458945784014 +15290525376551717170 +1559626750 +68682184 +12689613402799605860 +527434500 +517538547 +3542979343701772038 +447112610911 +163578521 +326737659857 +30458205707109873 +2625321606595479619 +498702419026 +555090760 +11846037961957312985 +2286775792223980496 +2676819007 +11599686562536949325 +3968978683605551949 +5831598103022077418 +15175534989820758889 +3812049126336301758 +545194794 +12348736218027264207 +12743882002561631754 +12318365723906541324 +8882845388820581451 +12769623874203027091 +1732546160493595960 +10430737389551487761 +9512531412808567772 +21433723812579518 +812123024 +9140909979694467183 +4025048830681353606 +1873618489039455401 +18331530485106038 +5516046791188875281 +6156456003434055463 +12474564753552836994 +17621561863500597513 +104202820 +29612220986426501 +1996555300 +2625321610894247837 +17489156252859434801 +103179363763095696 +15920335005095365860 +13112992413209136128 +2034107431 +17291573824845253535 +9772926989806013640 +819987397 +17170714 +1873618467543321286 +16156684754098128751 +6925759830950740072 +7274680 +16161820259100396848 +3698377064120454404 +10296839827164892306 +13913370016116443160 +1363739614 +92275213 +210659444315 +1784112314702629632 +5461104765611674055 +507299956084 +13237708552781955078 +197067432 +4211147846 +14657391675119111356 +25035091 +1735459858 +15139069 +14426056237756189706 +12771845711499103316 +9940375093616053431 +6523880655054768550 +62587308 +10967349376607587326 +1873618497636993704 +15290807392954681807 +5243033 +1133620917580466754 +1873618523431898109 +11613165301442872555 +282067642 +9870724569498781 +2141513421469058406 +14318336791419094928 +5885976069999102359 +6153917830015027393 +214958212644 +548995910 +90243587 +16101055855214332856 +9409295256684857617 +539099930 +30458248699119542 +23003457 +252379820 +6173800107753209956 +70451678 +13107433 +815924131 +1873618476140856959 +3188833133853148985 +3211386 +60555668 +5514354727165429372 +18430745393540238720 +5566476498435442740 +8821966780582857359 +806028152 +31022281504130688 +15273884660262766886 +17153706162049649384 +15568274631689570656 +98107936 +9870724567468020 +2625321602296449309 +5250413516934940017 +10377197347619277484 +546964288 +2429420595 +68420036 +13840095604897025041 +11075790 +1873618506234530930 +517276402 +31304293607146613 +10225919150420460684 +32714392818354350 +163316374 +17480593072628501093 +3653991426073234491 +28202143271093720 +2625321606595217579 +669516658 +11075097734987253589 +544932649 +5248951136269502637 +24535874148371011 +5247593352907000017 +13750803869111880047 +821756878 +5565348488711963913 +18940198 +23407778443822783 +811860878 +3910652327921846506 +2372569380647405649 +6151097721875664077 +8481290603310483360 +15289115311734721621 +5197393238738928914 +8858552325786961082 +15270695523793439937 +103940672 +6206603741566403719 +151388766 +2531021385567766485 +7563081637033018620 +13044533461222491710 +6154199872212897041 +9126223058424237061 +1160107295621122785 +32714349826081871 +6152225697206437786 +4333982245204396969 +7012532 +5411521012994803182 +5249797159683425776 +570557265 +17619527108083517000 +3758799224970808644 +11069796609748044689 +210659181949 +14926165161459649868 +7570985824906512457 +3234866947851553000 +1906986264008723742 +24772943 +1873618446046923526 +7516607870825792868 +14876921 +72221177 +18411699906768535578 +1495925747 +62325160 +288043895627 +31304259214443724 +3685635809078676834 +4980885 +313838798363 +13951205954302051853 +464309454125 +7151957518376504179 +6153353870293665804 +365428606574 +14319322726341872694 +3493083035910933027 +214957950334 +13222096480399396057 +22741311 +538837783 +12845285 +1675756474409617568 +7676326031729298383 +1873618476140594617 +70189530 +2861086850442987769 +12590629664748537952 +15501473033754248808 +1733166096 +2949238 +5833854255738587405 +6405261049027955879 +60293520 +6364097417622914469 +50397573 +15289397310941170468 +1436145094782551981 +9870724567205432 +155189878 +7996312456522828750 +2413828615876118471 +1818166298 +97845788 +2625321602296187261 +4451323549999957434 +3544953467117898450 +40501610 +6364097443417820330 +1543385872365455415 +12606726616442537392 +16436379939763522008 +7562235540534921217 +546702141 +20709678 +18413109962987470918 +10939233345785957508 +1384869222252743071 +14383042897579063 +245051624454 +813630359 +5881866613803452649 +1455946274504313841 +68157888 +10813643 +4502606072414800438 +9388513432576593267 +517014256 +16739161091967945306 +6203168539198949844 +20305658202031811 +15122676476569913436 +48365955 +5941764144784016877 +12601357272775920269 +5900805793554762144 +163054228 +6155327937823509637 +95814162 +2625321606594955469 +544670501 +11092808190891527547 +6365225423046182853 +3545799490531822688 +5991347927496329957 +2676033356038473537 +6928358494714596151 +18895516000586505 +18413109993081143373 +1317798870 +3242943116712479419 +8468495303965871404 +10215782083327823122 +295544243748734701 +7536133444401891169 +13880529192106527090 +18412263930975748140 +103678524 +8816997365064994109 +5513226652957347114 +13427220419978791304 +4279895118 +2581508047683782932 +151126621 +16436648502584675667 +5245789596497153220 +18411417868870352907 +1574831104 +5512098613140196086 +16646420 +16881311723980129501 +580191075 +6750384 +460010423829 +17142588721119759321 +5411521012994540776 +13331692090551241408 +2236213724530672835 +10512763733196344280 +91750922 +493159123 +210658919829 +5353476789791099071 +2973047420892220660 +102615266471184862 +817431474 +71959029 +14614773 +29330157293667421 +18411417898964025362 +8854886129749066875 +62063012 +1631882651478526261 +1873618497636468806 +1626046306171619904 +4718737 +6971710725545264615 +15463390673086056969 +5996988225456246061 +2625321597997156982 +1258091056198584472 +2365498112266798670 +12258209558853782455 +548471621 +200191596416994196 +5565348480113903112 +10159392401199270768 +538575636 +5782296448490211725 +15289115277341755866 +12583138 +4959080478982475006 +4237766475632413481 +2687090 +60031373 +11241814380293784908 +18413674017288355935 +10162787574158199843 +5625593289148533238 +605557034314828631 +2625321602295925195 +97583640 +16546579671803956126 +546439994 +13513914891881875478 +18412827955182960702 +18142877345697171235 +8716776878113885241 +5991347923197297866 +21715680028265805 +5299098848608717979 +2686971790050919863 +10551496 +2676033351739442523 +5246976935469649046 +4236074403011431549 +5561348123192067576 +516752111 +13525196865559988902 +451412624470 +6813843502384089093 +3452050537366752044 +2723374776553770162 +105448017 +14284319595218536933 +356832576945 +1987904546 +2789363555876800106 +17063697102470777209 +6584302816815089825 +5727354422913010657 +13944415416121166662 +28311895 +11906248855590275274 +3707523343842937215 +18412827985276633157 +821232589 +18415907 +2676033356038210923 +17257283880273643533 +18331556279224644 +9117971362513815455 +18411981923171237924 +309541536868 +113312346 +46072191 +103416376 +27920126869375123 +160760449 +361131345578 +9234597529149245860 +14835085562484362568 +4585257123188181630 +1413046597527538184 +6208295874376239521 +13217980679449939250 +1966081057 +6101795981361546864 +16384272 +10370417990725208293 +4196703391028741586 +6488236 +63832509 +5153885660580611393 +6155045912821630127 +5197393273132877515 +2625321593698126810 +10720606758114626648 +9870724570745030 +30740204914804024 +91488775 +7792373120121047026 +3579577413 +5458848587100981064 +755605599842665887 +17404805271631431757 +417019921504 +9386257335747873389 +817169327 +18413391979390173264 +71696881 +8328637003859953646 +14665059300281706 +6101796011455220816 +4456589 +13070886371126478108 +8733200714257204941 +10913926882465549337 +29330183088310857 +61800865 +14949273699027977966 +1873618523431110190 +3573803894998305775 +5569296709751605280 +5835546375651263675 +9870724568714358 +42008942 +1746899701160150410 +9664889374910385451 +7406761759861377295 +2625321597996894992 +365428082633 +11888218815508973537 +6311975551774360856 +1408369638 +6101795942670075923 +15515140772745448064 +27638058877519937 +13361048879788721990 +2430665780 +22217020 +538313489 +927164962728314711 +69665238 +27638084672424186 +2573543627316201844 +12320990 +2424942 +18413392009483845719 +3660444556051220001 +18412545947378450486 +154665586 +9870724566681132 +546177847 +2229804632046437624 +5245848917148372136 +15906307047154976446 +827351178595273968 +5780604350074062990 +6350640494756627870 +9198943117821938833 +2676033351739180486 +1192315303887243384 +67633599 +6205475723246636047 +17419818910382754661 +162529937 +17083693235326683482 +105185869 +8912366315847026281 +5249797202674912471 +2446394423 +1461650414 +257426098 +17299513133793348673 +4451048243670025981 +14597841535548131734 +14130457194541352666 +15290525359355331959 +9195012299735698785 +524354306 +429916226796 +6153353788611431303 +1728578573 +6153071806602085789 +2676033356037948725 +8257735 +2785415326238575484 +1873618489038408278 +8072726556923202784 +7731878007432940921 +16271603835638319461 +11229884474259868248 +5835546388547569431 +2704904949958969710 +103154228 +2625321589399096275 +6887529782530082437 +45810044 +16365628939578247566 +4408861808311732424 +3554388240579364748 +3431353251379022211 +4131548706499659810 +3229097897723824621 +818938814 +16122124 +10831084194895235709 +6226088 +6366071472254485645 +10441809166173275876 +9538952396691934382 +5994450030541998229 +6835382734606174906 +4397798273518472097 +2625321593697864817 +9870724570481756 +17782439637510195701 +31304332299601191 +4074350515307087985 +10758418391935682553 +11405246090117384413 +196018851 +17943317531894613402 +15289397375426759758 +1801651221 +12716605781588708278 +5353476789790574588 +1873618450346936800 +14462121002204464918 +2785415309041207732 +71434733 +10770155859627543824 +1873618476141841211 +5780604362970367638 +2530739313276357975 +14090480 +5567604589840172352 +296644709200 +11266915032714840583 +4194441 +2200512120787569683 +2549492329236335496 +6211116016906930204 +99090988 +9625506809262378259 +13237708535585570818 +490103571663 +14541340640523322842 +9870724568450966 +1793158821936040552 +9486667438472824267 +21954873 +538051341 +1398211555 +5408700909154273182 +5356297014005859746 +8444237263823374707 +69403090 +2599235317101562153 +15897859265386515143 +6097847713031849822 +2162794 +9796067026192895123 +13117159209037203716 +164299420 +17088031212435737557 +8099682237308012832 +8971880411373045432 +3099205763721988894 +9870724566418979 +545915701 +13237708565679243273 +4449074137450482853 +18115860927276518423 +5247593352907982888 +16533468055605152863 +1873618458944474091 +19923244 +3188833116656765520 +2676033351738918494 +4501477955215362649 +17621268784989013395 +14581169549127125939 +6206321707968234614 +33278352538406314 +516227820 +6890349946557761313 +1411918553413126104 +162267790 +2474797953316292924 +1694703987789596868 +18172096623373846790 +28766090095429261 +1223976979390989739 +3221822110943152678 +104923721 +15185362616787929146 +10003084053115964048 +2625321585100065781 +437798118096833445 +1815348248 +31304323701802109 +152371807 +14046027923586223423 +2021331689141374237 +20869691006257762 +13044533461223476582 +16778219695595128445 +12057002331826554305 +17465760298758178660 +7576852735584046364 +129168850403198609 +820708298 +17891616 +1873618489038145001 +7995587 +11911353550167017696 +4522983015860209939 +12612941966326959190 +102892081 +2625321589398833886 +45547899 +11548493110908749415 +4076606693818764590 +7851156332894489575 +12779163922391107832 +5991347884505304103 +1095239150174145285 +3863606920688567965 +10771469979967884371 +15859976 +14312864964518020808 +17245750799710423012 +5963940 +10655291933708585535 +4162099616697747321 +63308215 +1873618519131818153 +30176189305784773 +53412232 +318140582948 +15611911946388048179 +12640696470018459947 +30176223702288623 +9870724570219682 +33278412725750974 +1409876968 +28766150282773591 +1873618450346674286 +15290243360148359553 +14036340911856223966 +6365225461738636619 +816645035 +417019398489 +6206321673575531611 +12057284352529139627 +71172585 +13828334 +7528870385169533979 +5832726134240118664 +2785415334835848520 +2572415553107265488 +61276571 +3932293 +9870724568188981 +1873618549225491555 +2360543918673038210 +98828841 +12512221777814685432 +17939922315943150958 +6045857707735386835 +21692726 +4502324038816629924 +11490081257974859839 +17639632887023929831 +1316357237551401394 +6101795994259359091 +11796695 +69140942 +18411699889572151318 +12074216556992400767 +1320813529 +8618954206934993224 +164037275 +4160546838840674266 +12591757708863407913 +555549513 +9870724566156739 +154141293 +32714414313178248 +545653553 +223556471268 +12613788024133322735 +812581780 +5778348150066318224 +1500709877 +6741138607599781046 +9227353569080969220 +515965674 +13884327378110449525 +18411699919665823773 +16340493341965880015 +162005644 +620757861 +21997756618049241 +17007720368052373541 +13001845694847518363 +227855238971 +17629469 +1737950228 +9288263741171697848 +20305615210743190 +1873618489037883086 +18613533990193666 +7733439 +313841551493 +15288551330518206781 +17302333254828493968 +6153071832396467338 +2979056014524680527 +8857706336766199103 +2625321589398571980 +45285754 +5991347884505041337 +4502324004423927097 +16874702537456224943 +14911447610171655366 +13944990587222231178 +3308118261903721908 +18413109975884759113 +8412057600244518110 +15597828 +2538734651 +818414521 +17082847207615236134 +18276979644936029994 +5701792 +63046067 +5882159696614657105 +1410790466305853323 +18412263913779363880 +32714379920475611 +539325825270679628 +1873618519131556994 +13536993689470216 +9870724569957729 +43254135 +5153885686374731086 +9387385384162626351 +8336200085500660803 +5303047104041388600 +5512098595943810546 +5717788221838658971 +2324121364801391676 +12012735189037878155 +2192639020 +1873618476141316771 +70910437 +3670145 +2219404100148201532 +2544580112253650683 +61014424 +6155045921420412650 +18412263943873036335 +1873618549225229533 +9870724567926898 +98566694 +29894215892535509 +155910777 +6366353527348399255 +9956242218935388443 +31586340104504804 +219257441372 +13522668389390157414 +18411417881767641102 +11534547 +279448847671 +7242736046355514492 +68878794 +814351263 +1192315299587689576 +2524775482 +34124461934314600 +507839197 +5539270545646881104 +4974759074281293673 +5337229686545450161 +153879145 +12644080653952551280 +30458205707308380 +545391405 +17877509356004052233 +17520266449292560845 +11065487246536017596 +2011949215506761725 +6155045882728942511 +812319634 +1130753852548581517 +573047641 +5299098874402571932 +18413674000091971675 +18331556280207363 +17269866578628118199 +15289397293744523027 +161743496 +10649664295314066054 +6051485356288903427 +4347925833116091776 +30458188511970924 +104399431 +10184384893691038634 +7401639761433855789 +1308623824 +563151692 +2625321610894444316 +7239069803025663720 +11434534198373320614 +1873618441748613384 +5622264654903379074 +29330122899915877 +15636380174699072146 +820184006 +2597848126 +10233694917695638297 +14585410861575638263 +7471291 +85348920764927349 +6366353492955694732 +18413674030185644130 +4127600472562141528 +35127645 +5780604337176709161 +541328159 +2524806001290315567 +13850612818404510827 +18412827968080248897 +15335680 +3493395603981665996 +17858552114457937219 +62783919 +3875793754648151904 +5564423899624572258 +292345154665 +3489447322753895731 +18411981905974853664 +5439644 +42991988 +9870724569695611 +12269921124804135698 +559088458 +33278386930321618 +15289397353931868100 +214958409445 +6219166245997316001 +15289397379726773461 +30458248699315998 +23200068 +12163381674616883890 +70648289 +9000175594581527004 +806224763 +89657146100418951 +15475002888547338265 +3407997 +60752278 +18411981936068526119 +14267039342724252928 +13726068525522684375 +1873618527730862181 +4504298213822565083 +155648632 +98304546 +9870724567665640 +13681696359428851594 +219257178788 +24535844054893958 +50011031689890353 +10532987940533372886 +11272401 +23407795639356361 +68616647 +814089116 +15635925519041823968 +1998521381 +163512984 +797977540607610221 +32150286927595340 +4709060078846741586 +5967447917778832244 +5885976078596834724 +2625321606595414132 +153616999 +1744643526947965735 +17461812017531651650 +987047180239768912 +30740239306197230 +15288833278135765839 +525337347 +5885976155981547843 +18413391992287461459 +10532987970627045461 +56689033 +5722409915131627177 +114033243 +10159956468397444373 +18412545930182066226 +5349367342193968413 +13819010092172884 +104137283 +17953636526298302297 +2224234517276395067 +2789363555875490728 +2625321610894182276 +12426051065400527122 +9355193091131312182 +30740222110861163 +14361095630442006439 +3137288237381257087 +17105177 +819921860 +7209143 +1727529996 +810025856 +805679481429165719 +17298949057997047589 +21997713627284659 +16120716880803858984 +33560368941433940 +1535706104 +10229733804179524009 +18412545960275738681 +9714916620294556051 +4078298775038527628 +5461104765611607541 +210659378559 +92209676 +13418544886826534789 +14264208172476401284 +1917322269 +197001895 +24969554 +5405598728725530322 +15073532 +817890229 +72417787 +1873618471842024407 +17091318705916150977 +5946696443085589628 +5177496 +5847102830955857465 +62521771 +1873618523431831649 +5835546371351184527 +14824583848163281869 +42729843 +9870724569433729 +5780604315680310424 +16385074671182940805 +214958147231 +3007753865419557454 +491586249 +17943317531893566468 +1801912319444323213 +22937920 +539034393 +27356055371580547 +1873618476140792146 +5198803303557629187 +6103488088376871190 +13041896 +1733362705 +70386141 +2306802734 +643826540 +3145849 +14637903957824965363 +519242494 +60490131 +805962615 +5784522635265967958 +1873618527730601376 +18301216972082383618 +11644189250161151139 +2625321602296383846 +9870724567402585 +98042399 +15741861301866530650 +494403323033 +6729754102968812754 +546898751 +6208295835683456476 +33560403333875446 +14409153078548760239 +15530271666638163275 +1873618458945456185 +16951650337051970851 +5144036663261072615 +813826970 +12133908888583014197 +68354499 +11010253 +279448324634 +14749580058850363919 +6633286351216577743 +2089265852158774334 +8929038315166239946 +31586271318836879 +13678484518713821516 +105906772 +96010773 +2625321606595152102 +153354852 +10831360821402142464 +5652457623480305518 +8503320935775669540 +16483453074211931840 +363084051790629688 +544867112 +258146996 +5944020284604679310 +5782296431293302176 +28484176870181368 +23407778443758207 +3973491023432910866 +5778348175860436286 +1873618514834032208 +5438906422044199526 +103875135 +7697026996393675938 +1709507593 +161219206 +13237708548482859013 +3701601059573925529 +879419277503368073 +3822179681402096264 +5565348445721659362 +532291916112267238 +256115374 +1460339693 +13351948495571782591 +14665351642484132 +3008657884776564221 +2341393787733871788 +16904712944497920326 +3967850626592737364 +16843031 +4131548702199581670 +6946995 +809763710 +1928986057181235415 +11964228788262537512 +2989761681675848960 +1873618519132801026 +7276444624641068235 +5994450030542718433 +12284124821458521275 +111739480 +4076606646528706921 +13650504529854072320 +15804734059994287439 +14425661019905001872 +2395604016 +14465116522071263669 +210659116497 +15290243360149343057 +15777957523720635747 +10167863869407233224 +18331517588211470 +12884708026702235763 +14811384 +72155640 +7042731044489660311 +15288269305517836796 +5675796551176948530 +14264208198271043974 +1495860210 +5787083718919720300 +25099894056749168 +683965395648908415 +62259623 +4915348 +12974919760129952993 +6155045917120857525 +1873618523431569790 +9013091190501541709 +4392112055939237960 +2625321597997353452 +15897908900500866947 +6177363174264606048 +15872788267758849077 +491324104 +33560399034844286 +22675774 +17542946455516547053 +2431124533 +538772246 +27920040887322186 +8704274751914773568 +12085352355710699032 +6153353775713551670 +70123993 +27356081166223293 +7885152524183078888 +60227983 +2883701 +11700344903086704893 +7329667560521271617 +518980348 +5833854255738521265 +8618954206935976415 +3901910077209972079 +1713308683 +1992881785903908578 +4530582984922301900 +16130159995999161574 +155124341 +2625321602296121720 +1884114794138700522 +5778348218852443426 +97780251 +4240022615453076686 +6097847786116483627 +6361518319333476776 +30540122 +28484146776247610 +546636604 +5741055947585816645 +6100103891543657570 +8807886331112851129 +813564822 +10223260478367337870 +746324852 +15287423226215073909 +11226550812567014265 +1491796976 +8097653480026868144 +5995296157134227520 +1873618532029106835 +1539245050 +48300418 +331037869860 +95748625 +6314795724398267312 +5888081980883929307 +544604964 +34124418943289166 +5245848947242502849 +32432363517642192 +2676033356038407648 +811533196 +1317733333 +8920676095134336910 +17149817495305717193 +918014392040164136 +103612987 +8695136395555507435 +18349504802666319185 +14847634415788362123 +1584661506 +4287350266942457603 +525512494730316455 +5881302580997523790 +1574765567 +3784125305237867347 +819397570 +8326286517935867839 +16149105318148965958 +16580883 +6684847 +18411699902469439513 +11229983338076703492 +15292499491369977714 +339635406848 +9870724570940976 +100 +101 +102 diff --git a/python/cudf/cudf/tests/data/subword_tokenizer_data/bert_base_cased_sampled/vocab.txt b/python/cudf/cudf/tests/data/subword_tokenizer_data/bert_base_cased_sampled/vocab.txt new file mode 100644 index 00000000000..57c08778e36 --- /dev/null +++ b/python/cudf/cudf/tests/data/subword_tokenizer_data/bert_base_cased_sampled/vocab.txt @@ -0,0 +1,3500 @@ +[PAD] +[unused1] +[unused2] +[unused3] +[unused4] +[unused5] +[unused6] +[unused7] +[unused8] +[unused9] +[unused10] +[unused11] +[unused12] +[unused13] +[unused14] +[unused15] +[unused16] +[unused17] +[unused18] +[unused19] +[unused20] +[unused21] +[unused22] +[unused23] +[unused24] +[unused25] +[unused26] +[unused27] +[unused28] +[unused29] +[unused30] +[unused31] +[unused32] +[unused33] +[unused34] +[unused35] +[unused36] +[unused37] +[unused38] +[unused39] +[unused40] +[unused41] +[unused42] +[unused43] +[unused44] +[unused45] +[unused46] +[unused47] +[unused48] +[unused49] +[unused50] +[unused51] +[unused52] +[unused53] +[unused54] +[unused55] +[unused56] +[unused57] +[unused58] +[unused59] +[unused60] +[unused61] +[unused62] +[unused63] +[unused64] +[unused65] +[unused66] +[unused67] +[unused68] +[unused69] +[unused70] +[unused71] +[unused72] +[unused73] +[unused74] +[unused75] +[unused76] +[unused77] +[unused78] +[unused79] +[unused80] +[unused81] +[unused82] +[unused83] +[unused84] +[unused85] +[unused86] +[unused87] +[unused88] +[unused89] +[unused90] +[unused91] +[unused92] +[unused93] +[unused94] +[unused95] +[unused96] +[unused97] +[unused98] +[unused99] +[UNK] +[CLS] +[SEP] +[MASK] +[unused100] +[unused101] +! +" +# +$ +% +& +' +( +) +* ++ +, +- +. +/ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +: +; +< += +> +? +@ +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +[ +\ +] +^ +_ +` +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +{ +| +} +~ +¡ +¢ +£ +¥ +§ +¨ +© +ª +« +¬ +® +° +± +² +³ +´ +µ +¶ +· +¹ +º +» +¼ +½ +¾ +¿ +À +Á + +Ä +Å +Æ +Ç +È +É +Í +Î +Ñ +Ó +Ö +× +Ø +Ú +Ü +Þ +ß +à +á +â +ã +ä +å +æ +ç +è +é +ê +ë +ì +í +î +ï +ð +ñ +ò +ó +ô +õ +ö +÷ +ø +ù +ú +û +ü +ý +þ +ÿ +Ā +ā +ă +ą +Ć +ć +Č +č +ď +Đ +đ +ē +ė +ę +ě +ğ +ġ +Ħ +ħ +ĩ +Ī +ī +İ +ı +ļ +Ľ +ľ +Ł +ł +ń +ņ +ň +ŋ +Ō +ō +ŏ +ő +Œ +œ +ř +Ś +ś +Ş +ş +Š +š +Ţ +ţ +ť +ũ +ū +ŭ +ů +ű +ų +ŵ +ŷ +ź +Ż +ż +Ž +ž +Ə +ƒ +ơ +ư +ǎ +ǐ +ǒ +ǔ +ǫ +Ș +ș +Ț +ț +ɐ +ɑ +ɔ +ɕ +ə +ɛ +ɡ +ɣ +ɨ +ɪ +ɲ +ɾ +ʀ +ʁ +ʂ +ʃ +ʊ +ʋ +ʌ +ʐ +ʑ +ʒ +ʔ +ʰ +ʲ +ʳ +ʷ +ʻ +ʼ +ʾ +ʿ +ˈ +ː +ˡ +ˢ +ˣ +́ +̃ +̍ +̯ +͡ +Α +Β +Γ +Δ +Ε +Η +Θ +Ι +Κ +Λ +Μ +Ν +Ο +Π +Σ +Τ +Φ +Χ +Ψ +Ω +ά +έ +ή +ί +α +β +γ +δ +ε +ζ +η +θ +ι +κ +λ +μ +ν +ξ +ο +π +ρ +ς +σ +τ +υ +φ +χ +ψ +ω +ό +ύ +ώ +І +Ј +А +Б +В +Г +Д +Е +Ж +З +И +К +Л +М +Н +О +П +Р +С +Т +У +Ф +Х +Ц +Ч +Ш +Э +Ю +Я +а +б +в +г +д +е +ж +з +и +й +к +л +м +н +о +п +р +с +т +у +ф +х +ц +ч +ш +щ +ъ +ы +ь +э +ю +я +ё +і +ї +ј +њ +ћ +Ա +Հ +ա +ե +ի +կ +մ +յ +ն +ո +ս +տ +ր +ւ +ְ +ִ +ֵ +ֶ +ַ +ָ +ֹ +ּ +א +ב +ג +ד +ה +ו +ז +ח +ט +י +כ +ל +ם +מ +ן +נ +ס +ע +פ +צ +ק +ר +ש +ת +، +ء +آ +أ +إ +ئ +ا +ب +ة +ت +ث +ج +ح +خ +د +ذ +ر +ز +س +ش +ص +ض +ط +ظ +ع +غ +ف +ق +ك +ل +م +ن +ه +و +ى +ي +َ +ِ +ٹ +پ +چ +ک +گ +ہ +ی +ے +ं +आ +क +ग +च +ज +ण +त +द +ध +न +प +ब +भ +म +य +र +ल +व +श +ष +स +ह +ा +ि +ी +ु +े +ो +् +। +॥ +আ +ই +এ +ও +ক +খ +গ +চ +ছ +জ +ট +ত +থ +দ +ধ +ন +প +ব +ম +য +র +ল +শ +স +হ +় +া +ি +ী +ু +ে +ো +্ +য় +க +த +ப +ம +ய +ர +ல +வ +ா +ி +ு +் +ร +་ +ག +ང +ད +ན +བ +མ +ར +ལ +ས +ི +ུ +ེ +ོ +ა +ე +ი +ლ +ნ +ო +რ +ს +ᴬ +ᴵ +ᵀ +ᵃ +ᵇ +ᵈ +ᵉ +ᵍ +ᵏ +ᵐ +ᵒ +ᵖ +ᵗ +ᵘ +ᵢ +ᵣ +ᵤ +ᵥ +ᶜ +ᶠ +ḍ +Ḥ +ḥ +Ḩ +ḩ +ḳ +ṃ +ṅ +ṇ +ṛ +ṣ +ṭ +ạ +ả +ấ +ầ +ẩ +ậ +ắ +ế +ề +ể +ễ +ệ +ị +ọ +ố +ồ +ổ +ộ +ớ +ờ +ợ +ụ +ủ +ứ +ừ +ử +ữ +ự +ỳ +ỹ +ἀ +ἐ +ὁ +ὐ +ὰ +ὶ +ὸ +ῆ +ῖ +ῦ +ῶ +‐ +‑ +‒ +– +— +― +‖ +‘ +’ +‚ +“ +” +„ +† +‡ +• +… +‰ +′ +″ +⁄ +⁰ +ⁱ +⁴ +⁵ +⁶ +⁷ +⁸ +⁹ +⁺ +⁻ +ⁿ +₀ +₁ +₂ +₃ +₄ +₅ +₆ +₇ +₈ +₉ +₊ +₍ +₎ +ₐ +ₑ +ₒ +ₓ +ₕ +ₖ +ₘ +ₙ +ₚ +ₛ +ₜ +₤ +€ +₱ +₹ +ℓ +№ +ℝ +⅓ +← +↑ +→ +↔ +⇌ +⇒ +∂ +∈ +− +∗ +∘ +√ +∞ +∧ +∨ +∩ +∪ +≈ +≠ +≡ +≤ +≥ +⊂ +⊆ +⊕ +⋅ +─ +│ +■ +● +★ +☆ +☉ +♠ +♣ +♥ +♦ +♭ +♯ +⟨ +⟩ +ⱼ +、 +。 +《 +》 +「 +」 +『 +』 +〜 +い +う +え +お +か +き +く +け +こ +さ +し +す +せ +そ +た +ち +つ +て +と +な +に +の +は +ひ +ま +み +む +め +も +や +ゆ +よ +ら +り +る +れ +ん +ア +ィ +イ +ウ +エ +オ +カ +ガ +キ +ク +グ +コ +サ +シ +ジ +ス +ズ +タ +ダ +ッ +テ +デ +ト +ド +ナ +ニ +ハ +バ +パ +フ +ブ +プ +マ +ミ +ム +ャ +ュ +ラ +リ +ル +レ +ロ +ン +・ +ー +一 +三 +上 +下 +中 +事 +二 +井 +京 +人 +亻 +仁 +佐 +侍 +光 +公 +力 +北 +十 +南 +原 +口 +史 +司 +吉 +同 +和 +囗 +国 +國 +土 +城 +士 +大 +天 +太 +夫 +女 +子 +宀 +安 +宮 +宿 +小 +尚 +山 +島 +川 +州 +平 +年 +心 +愛 +戸 +文 +新 +方 +日 +明 +星 +書 +月 +木 +本 +李 +村 +東 +松 +林 +正 +武 +氏 +水 +氵 +江 +河 +海 +版 +犬 +王 +生 +田 +白 +皇 +省 +真 +石 +社 +神 +竹 +美 +義 +花 +藤 +西 +谷 +車 +辶 +道 +郎 +郡 +部 +野 +金 +長 +門 +陽 +青 +食 +馬 +高 +龍 +龸 +사 +씨 +의 +이 +한 +fi +fl +! +( +) +, +- +/ +: +the +of +and +to +in +was +The +is +for +as +on +with +that +##s +his +by +he +at +from +it +her +He +had +an +were +you +be +In +she +are +but +which +It +not +or +have +my +him +one +this +me +has +also +up +their +first +out +who +been +they +She +into +all +would +its +##ing +time +two +##a +##e +said +about +when +over +more +other +can +after +back +them +then +##ed +there +like +so +only +##n +could +##d +##i +##y +what +no +##o +where +This +made +than +if +You +##ly +through +we +before +##r +just +some +##er +years +do +New +##t +down +between +new +now +will +three +most +On +around +year +used +such +being +well +during +They +know +against +under +later +did +part +known +off +while +His +re +... +##l +people +until +way +American +didn +University +your +both +many +get +United +became +head +There +second +As +work +any +But +still +again +born +even +eyes +After +including +de +took +And +long +team +season +family +see +right +same +called +name +because +film +don +10 +found +much +school +##es +going +won +place +away +We +day +left +John +000 +hand +since +World +these +how +make +number +each +life +area +man +four +go +No +here +very +National +##m +played +released +never +began +States +album +home +last +too +held +several +May +own +##on +take +end +School +##h +ll +series +What +want +use +another +city +When +2010 +side +At +may +That +came +face +June +think +game +those +high +March +early +September +##al +2011 +looked +July +state +small +thought +went +January +October +##u +based +August +##us +world +good +April +York +us +12 +2012 +2008 +For +2009 +group +along +few +South +little +##k +following +November +something +2013 +December +set +2007 +old +2006 +2014 +located +##an +music +County +City +former +##in +room +ve +next +All +##man +got +father +house +##g +body +15 +20 +18 +started +If +2015 +town +our +line +War +large +population +named +British +company +member +five +My +single +##en +age +State +moved +February +11 +Her +should +century +government +built +come +best +show +However +within +look +men +door +without +need +wasn +2016 +water +One +system +knew +every +died +League +turned +asked +North +St +wanted +building +received +song +served +though +felt +##ia +station +band +##ers +local +public +himself +different +death +say +##1 +30 +##2 +2005 +16 +night +behind +children +English +members +near +saw +together +son +14 +voice +village +13 +hands +help +##3 +due +French +London +top +told +open +published +third +2017 +play +across +During +put +final +often +include +25 +##le +main +having +2004 +once +ever +let +book +led +gave +late +front +find +club +##4 +German +included +species +College +form +opened +mother +women +enough +West +must +2000 +power +really +17 +making +half +##6 +order +might +##is +given +million +times +days +point +full +service +With +km +major +##7 +original +become +seen +II +north +six +##te +love +##0 +national +International +##5 +24 +So +District +lost +run +couldn +career +always +##9 +2003 +##th +country +##z +House +air +tell +south +worked +woman +player +##A +almost +war +River +##ic +married +continued +Then +James +close +black +short +##8 +##na +using +history +returned +light +car +##ra +sure +William +things +General +##ry +2002 +better +support +100 +among +From +feet +King +anything +21 +19 +established +district +2001 +feel +great +##ton +level +Cup +These +written +games +others +already +title +story +##p +law +thing +US +record +role +however +By +students +England +white +control +least +inside +land +##C +22 +give +community +hard +##ie +non +##c +produced +George +round +period +Park +business +various +##ne +does +present +wife +far +taken +per +reached +David +able +version +working +young +live +created +joined +East +living +appeared +case +High +done +23 +important +President +Award +France +position +office +looking +total +general +class +To +production +##S +football +party +brother +keep +mind +free +Street +hair +announced +development +either +nothing +moment +Church +followed +wrote +why +India +San +election +1999 +lead +How +##ch +##rs +words +European +course +considered +America +arms +Army +political +##la +28 +26 +west +east +ground +further +church +less +site +First +Not +Australia +toward +California +##ness +described +works +An +Council +heart +past +military +27 +##or +heard +field +human +soon +founded +1998 +playing +trying +##x +##ist +##ta +television +mouth +although +taking +win +fire +Division +##ity +Party +Royal +program +Some +Don +Association +According +tried +TV +Paul +outside +daughter +Best +While +someone +match +recorded +Canada +closed +region +Air +above +months +elected +##da +##ian +road +##ar +brought +move +1997 +leave +##um +Thomas +1996 +am +low +Robert +formed +person +services +points +Mr +miles +##b +stop +rest +doing +needed +international +release +floor +start +sound +call +killed +real +dark +research +finished +language +Michael +professional +change +sent +50 +upon +29 +track +hit +event +2018 +term +example +Germany +similar +return +##ism +fact +pulled +stood +says +ran +information +yet +result +developed +girl +##re +God +1995 +areas +signed +decided +##ment +Company +seemed +##el +co +turn +race +common +video +Charles +Indian +##ation +blood +art +red +##able +added +rather +1994 +met +director +addition +design +average +minutes +##ies +##ted +available +bed +coming +friend +idea +kind +Union +Road +remained +##ting +everything +##ma +running +care +finally +Chinese +appointed +1992 +Australian +##ley +popular +mean +teams +probably +##land +usually +project +social +Championship +possible +word +Russian +instead +mi +herself +##T +Peter +Hall +Center +seat +style +money +1993 +else +Department +table +Music +current +31 +features +special +events +character +Two +square +sold +debut +##v +process +Although +Since +##ka +40 +Central +currently +education +placed +lot +China +quickly +forward +seven +##ling +Europe +arm +performed +Japanese +1991 +Henry +Now +Dr +##ion +week +Group +myself +big +UK +Washington +ten +deep +1990 +Club +Japan +space +La +directed +smile +episode +hours +whole +##de +##less +Why +wouldn +designed +strong +training +changed +Society +stage +involved +hadn +towards +leading +police +eight +kept +Institute +study +largest +child +eventually +private +modern +Court +throughout +getting +originally +attack +##E +talk +Great +longer +songs +alone +##ine +wide +dead +walked +shot +##ri +Oh +force +##st +Art +today +friends +Island +Richard +1989 +center +construction +believe +size +White +ship +completed +##B +gone +Just +rock +sat +##R +radio +below +entire +families +league +includes +type +lived +official +range +hold +featured +Most +##ter +president +passed +means +##f +forces +lips +Mary +Do +guitar +##ce +food +wall +Of +spent +Its +performance +hear +##P +Western +reported +sister +##et +morning +##M +especially +##ive +Minister +itself +post +bit +groups +1988 +##tion +Black +##ng +Well +raised +sometimes +Canadian +Paris +Spanish +replaced +schools +Academy +leaving +central +female +Christian +Jack +whose +college +onto +provided +##D +##ville +players +actually +stopped +##son +Museum +doesn +##ts +books +fight +allowed +##ur +beginning +Records +awarded +parents +coach +##os +Red +saying +##ck +Smith +Yes +Lake +##L +aircraft +1987 +##ble +previous +ft +action +Italian +African +happened +vocals +Act +future +court +##ge +1986 +degree +phone +##ro +Is +countries +winning +breath +Love +river +matter +Lord +Other +list +self +parts +##ate +provide +cut +shows +plan +1st +interest +##ized +Africa +stated +Sir +fell +owned +earlier +ended +competition +attention +1985 +lower +nearly +bad +older +stay +Saint +##se +certain +1984 +fingers +blue +try +fourth +Grand +##as +king +##nt +makes +chest +movement +states +moving +data +introduced +model +date +section +Los +deal +##I +skin +entered +middle +success +Texas +##w +summer +island +##N +Republic +length +husband +1980 +##ey +reason +anyone +forced +via +base +500 +job +covered +Festival +Roman +successful +rights +cover +Man +writing +Ireland +##F +related +goal +takes +buildings +true +weeks +1983 +Because +opening +novel +ISBN +meet +gold +##ous +mid +km² +standing +Football +Chicago +shook +whom +##ki +1982 +Day +feeling +scored +boy +higher +Force +leader +heavy +fall +question +sense +army +Second +energy +meeting +themselves +kill +##am +board +census +##ya +##ns +mine +meant +market +required +battle +campaign +attended +approximately +Kingdom +runs +active +##ha +contract +clear +previously +health +1979 +Arts +complete +Catholic +couple +units +##ll +##ty +Committee +shoulder +sea +systems +listed +##O +caught +tournament +##G +northern +author +Film +Your +##men +holding +offered +personal +1981 +southern +artist +traditional +studio +200 +capital +##ful +regular +ask +giving +organization +month +news +Are +read +managed +helped +studied +student +defeated +natural +industry +Year +noted +decision +Government +quite +##id +smiled +1972 +Maybe +tracks +##ke +Mark +al +media +engine +hour +Their +relationship +plays +property +structure +1976 +ago +Hill +Martin +1978 +ready +Many +Like +Bay +immediately +generally +Italy +Greek +practice +caused +division +significant +Joseph +speed +Let +thinking +completely +1974 +primary +mostly +##field +##K +1975 +##to +Even +writer +##led +dropped +magazine +collection +understand +route +highest +particular +films +lines +network +Science +loss +carried +direction +green +1977 +location +producer +according +Women +Queen +neck +thus +independent +view +1970 +Angeles +Soviet +distance +problem +Board +tour +western +income +appearance +access +Mexico +nodded +street +surface +arrived +believed +Old +1968 +1973 +becoming +whether +1945 +figure +singer +stand +Following +issue +window +wrong +pain +everyone +lives +issues +park +slowly +la +act +##va +bring +Lee +operations +key +comes +fine +cold +famous +Navy +1971 +Me +additional +individual +##ner +Zealand +goals +county +contains +Service +minute +2nd +reach +talking +particularly +##ham +movie +Director +glass +paper +studies +##co +railway +standard +Education +45 +represented +Chief +Louis +launched +Star +terms +60 +1969 +experience +watched +Another +Press +Tom +staff +starting +subject +break +Virginia +nine +eye +##age +evidence +foot +##est +companies +Prince +##V +gun +create +Big +People +guy +Green +simply +numerous +##line +increased +twenty +##ga +##do +1967 +award +officer +stone +Before +material +Northern +grew +male +plant +Life +legs +step +Al +unit +35 +except +answer +##U +report +response +Edward +commercial +edition +trade +science +##ca +Irish +Law +shown +rate +failed +##ni +remains +changes +mm +limited +larger +Later +cause +waiting +Time +##wood +cost +Bill +manager +activities +likely +allow +operated +retired +##ping +65 +directly +Who +associated +effect +hell +Florida +straight +hot +Valley +management +girls +expected +eastern +Mike +chance +cast +centre +chair +hurt +problems +##li +walk +programs +Team +characters +Battle +edge +pay +maybe +corner +majority +medical +Joe +Summer +##io +attempt +Pacific +command +Radio +##by +names +municipality +1964 +train +economic +Brown +feature +sex +source +agreed +remember +Three +1966 +1965 +Pennsylvania +victory +senior +annual +III +Southern +results +Sam +serving +religious +Jones +appears +##der +despite +claimed +Both +musical +matches +fast +security +selected +Young +double +complex +hospital +chief +Times +##ve +Championships +filled +Public +Despite +beautiful +Research +plans +Province +##ally +Wales +##ko +artists +metal +nearby +Spain +##il +32 +houses +supported +piece +##no +stared +recording +nature +legal +Russia +##ization +remaining +looks +##sh +bridge +closer +cases +scene +marriage +Little +##é +uses +Earth +specific +Frank +theory +Good +discovered +referred +bass +culture +university +presented +Congress +##go +metres +continue +1960 +isn +Awards +meaning +cell +composed +separate +Series +forms +Blue +cross +##tor +increase +test +computer +slightly +Where +Jewish +Town +tree +status +1944 +variety +responsible +pretty +initially +##way +realized +pass +provides +Captain +Alexander +recent +score +broke +Scott +drive +financial +showed +Line +stories +ordered +soldiers +genus +operation +gaze +sitting +society +Only +hope +actor +follow +Empire +Yeah +technology +happy +focus +policy +spread +situation +##ford +##ba +Mrs +watch +Can +1963 +Commission +touch +earned +troops +Under +1962 +individuals +cannot +19th +##lin +mile +expression +exactly +suddenly +weight +dance +stepped +places +appear +difficult +Railway +anti +numbers +kilometres +star +##ier +department +ice +Britain +removed +Once +##lo +Boston +value +##ant +mission +trees +Order +sports +join +serve +Major +poor +Poland +mainly +Theatre +pushed +Station +##it +Lady +federal +silver +##ler +foreign +##ard +Eastern +##den +box +hall +subsequently +lies +acquired +1942 +ancient +CD +History +Jean +beyond +##ger +El +##les +growing +championship +native +Parliament +Williams +watching +direct +overall +offer +Also +80 +Secretary +spoke +Latin +ability +##ated +safe +presence +##ial +headed +regional +planned +1961 +Johnson +throat +consists +##W +extended +Or +bar +walls +Chris +stations +politician +Olympics +influence +share +fighting +speak +hundred +Carolina +die +stars +##tic +color +Chapter +##ish +fear +sleep +goes +Francisco +oil +Bank +sign +physical +##berg +Dutch +seasons +##rd +Games +Governor +sorry +lack +Centre +memory +baby +smaller +charge +Did +multiple +ships +shirt +Assembly +amount +leaves +3rd +Foundation +conditions +1943 +Rock +Democratic +Daniel +##at +winner +products +##ina +store +latter +Professor +civil +prior +host +1956 +soft +vote +needs +Each +rules +1958 +pressure +letter +normal +proposed +levels +records +1959 +paid +intended +Victoria +purpose +okay +historical +issued +1980s +broadcast +rule +simple +picked +firm +Sea +1941 +Elizabeth +1940 +serious +featuring +highly +graduated +mentioned +choice +1948 +replied +percent +Scotland +##hi +females +constructed +1957 +settled +Steve +recognized +cities +crew +glanced +kiss +competed +flight +knowledge +editor +More +Conference +##H +fifth +elements +##ee +##tes +function +newspaper +recently +Miss +cultural +brown +twice +Office +1939 +truth +Creek +1946 +households +USA +1950 +quality +##tt +border +seconds +destroyed +pre +wait +ahead +build +image +90 +cars +##mi +33 +promoted +professor +et +bank +medal +text +broken +Middle +revealed +sides +wing +seems +channel +1970s +Ben +loved +effort +officers +Will +##ff +70 +Israel +Jim +upper +fully +label +Jr +assistant +powerful +pair +positive +##ary +gives +1955 +20th +races +remain +kitchen +primarily +##ti +Sydney +easy +Tour +whispered +buried +300 +News +Polish +1952 +Duke +Columbia +produce +accepted +00 +approach +minor +1947 +Special +44 +Asian +basis +visit +Fort +Civil +finish +formerly +beside +leaned +##ite +median +rose +coast +effects +supposed +Cross +##hip +Corps +residents +Jackson +##ir +Bob +basketball +36 +Asia +seem +Bishop +Book +##ber +ring +##ze +owner +BBC +##ja +transferred +acting +De +appearances +walking +Le +press +grabbed +1954 +officially +1953 +##pe +risk +taught +review +##X +lay +##well +council +Avenue +seeing +losing +Ohio +Super +province +ones +travel +##sa +projects +equipment +spot +Berlin +administrative +heat +potential +shut +capacity +elections +growth +fought +Republican +mixed +Andrew +teacher +turning +strength +shoulders +beat +wind +1949 +Health +follows +camp +suggested +perhaps +Alex +mountain +contact +divided +candidate +fellow +34 +Show +necessary +workers +ball +horse +ways +questions +protect +gas +activity +younger +bottom +founder +Scottish +screen +treatment +easily +com +##house +dedicated +Master +warm +Night +Georgia +Long +von +##me +perfect +website +1960s +piano +efforts +##ide +Tony +sort +offers +Development +Simon +executive +##nd +save +Over +Senate +1951 +1990s +draw +master +Police +##ius +renamed +boys +initial +prominent +damage +Co +##ov +##za +online +begin +occurred +captured +youth +Top +account +tells +Justice +conducted +forest +##town +bought +teeth +Jersey +##di +purchased +agreement +Michigan +##ure +campus +prison +becomes +product +secret +guess +Route +huge +types +drums +64 +split +defeat +estate +housing +##ot +brothers +Coast +declared +happen +titled +therefore +sun +commonly +alongside +Stadium +library +Home +article +steps +telling +slow +assigned +refused +laughed +wants +Nick +wearing +Rome +Open +##ah +Hospital +pointed +Taylor +lifted +escape +participated +##j +drama +parish +Santa +##per +organized +mass +pick +Airport +gets +Library +unable +pull +Live +##ging +surrounding +##ries +focused +Adam +facilities +##ning +##ny +38 +##ring +notable +era +connected +gained +operating +laid +Regiment +branch +defined +Christmas +machine +Four +academic +Iran +adopted +concept +Men +compared +search +traffic +Max +Maria +greater +##ding +widely +##burg +serves +1938 +37 +Go +hotel +shared +typically +scale +1936 +leg +suffered +yards +pieces +Ministry +Wilson +episodes +empty +1918 +safety +continues +yellow +historic +settlement +400 +Come +Corporation +enemy +content +picture +evening +territory +method +trial +solo +driver +Here +##ls +entrance +Prize +spring +whatever +##ent +75 +##ji +reading +Arthur +##cy +Our +clothes +Prime +Illinois +Kong +code +##ria +sit +Harry +Federal +chosen +administration +bodies +begins +stomach +Though +seats +Hong +density +Sun +leaders +Field +museum +chart +platform +languages +##ron +birth +holds +Gold +##un +fish +combined +##ps +4th +1937 +largely +captain +trust +Game +van +boat diff --git a/python/cudf/cudf/tests/data/subword_tokenizer_data/test_sentences.txt b/python/cudf/cudf/tests/data/subword_tokenizer_data/test_sentences.txt new file mode 100644 index 00000000000..6111117192a --- /dev/null +++ b/python/cudf/cudf/tests/data/subword_tokenizer_data/test_sentences.txt @@ -0,0 +1,100 @@ +This text is included to make sure Unicode is handled properly: 力加勝北区ᴵᴺᵀᵃছজটডণত +This sample text is public domain and was randomly selected from Project Guttenberg. +The rain had only ceased with the gray streaks of morning at Blazing Star, and the settlement awoke to a moral sense of cleanliness, and the finding of forgotten knives, tin cups, and smaller camp utensils, where the heavy showers had washed away the debris and dust heaps before the cabin doors. +Indeed, it was recorded in Blazing Star that a fortunate early riser had once picked up on the highway a solid chunk of gold quartz which the rain had freed from its incumbering soil, and washed into immediate and glittering popularity. +Possibly this may have been the reason why early risers in that locality, during the rainy season, adopted a thoughtful habit of body, and seldom lifted their eyes to the rifted or india-ink washed skies above them. +"Cass" Beard had risen early that morning, but not with a view to discovery. +A leak in his cabin roof,--quite consistent with his careless, improvident habits,--had roused him at 4 A. M., with a flooded "bunk" and wet blankets. +The chips from his wood pile refused to kindle a fire to dry his bed-clothes, and he had recourse to a more provident neighbor's to supply the deficiency. +This was nearly opposite. +Mr. Cassius crossed the highway, and stopped suddenly. +But the Goblin could no longer sit quietly listening to the wisdom and intellect downstairs. No, as soon as the light shone in the evening from the attic it seemed to him as though its beams were strong ropes dragging him up, and he had to go and peep through the key-hole. There he felt the sort of feeling we have looking at the great rolling sea in a storm, and he burst into tears. He could not himself say why he wept, but in spite of his tears he felt quite happy. How beautiful it must be to sit under that tree with the student, but that he could not do; he had to content himself with the key-hole and be happy there! +But, wonderful to relate, not an irregular, shapeless fragment of crude ore, fresh from Nature's crucible, but a bit of jeweler's handicraft in the form of a plain gold ring. +Looking at it more attentively, he saw that it bore the inscription, "May to Cass." +Like most of his fellow gold-seekers, Cass was superstitious. +The fountain of classic wisdom, Hypatia herself. +As the ancient sage--the name is unimportant to a monk--pumped water nightly that he might study by day, so I, the guardian of cloaks and parasols, at the sacred doors of her lecture-room, imbibe celestial knowledge. +From my youth I felt in me a soul above the matter-entangled herd. +She revealed to me the glorious fact, that I am a spark of Divinity itself. +A fallen star, I am, sir!' continued he, pensively, stroking his lean stomach--'a fallen star!--fallen, if the dignity of philosophy will allow of the simile, among the hogs of the lower world--indeed, even into the hog-bucket itself. Well, after all, I will show you the way to the Archbishop's. +There is a philosophic pleasure in opening one's treasures to the modest young. +Perhaps you will assist me by carrying this basket of fruit?' And the little man jumped up, put his basket on Philammon's head, and trotted off up a neighbouring street. +Philammon followed, half contemptuous, half wondering at what this philosophy might be, which could feed the self-conceit of anything so abject as his ragged little apish guide; +but the novel roar and whirl of the street, the perpetual stream of busy faces, the line of curricles, palanquins, laden asses, camels, elephants, which met and passed him, and squeezed him up steps and into doorways, as they threaded their way through the great Moon-gate into the ample street beyond, drove everything from his mind but wondering curiosity, and a vague, helpless dread of that great living wilderness, more terrible than any dead wilderness of sand which he had left behind. +Already he longed for the repose, the silence of the Laura--for faces which knew him and smiled upon him; but it was too late to turn back now. +His guide held on for more than a mile up the great main street, crossed in the centre of the city, at right angles, by one equally magnificent, at each end of which, miles away, appeared, dim and distant over the heads of the living stream of passengers, the yellow sand-hills of the desert; +while at the end of the vista in front of them gleamed the blue harbour, through a network of countless masts. +At last they reached the quay at the opposite end of the street; +and there burst on Philammon's astonished eyes a vast semicircle of blue sea, ringed with palaces and towers. +He stopped involuntarily; and his little guide stopped also, and looked askance at the young monk, to watch the effect which that grand panorama should produce on him. +Nana also troubled him in another way. He had sometimes a feeling that she did not admire him. “I know she admires you tremendously, George,” +Mrs. Darling would assure him, and then she would sign to the children to be specially nice to father. Lovely dances followed, in which the only other servant, Liza, was sometimes allowed to join. +Such a midget she looked in her long skirt and maid's cap, though she had sworn, when engaged, that she would never see ten again. +The gaiety of those romps! +And gayest of all was Mrs. Darling, who would pirouette so wildly that all you could see of her was the kiss, and then if you had dashed at her you might have got it. +There never was a simpler happier family until the coming of Peter Pan. +Finally, I always go to sea as a sailor, because of the wholesome exercise and pure air of the fore-castle deck. +For as in this world, head winds are far more prevalent than winds from astern (that is, if you never violate the Pythagorean maxim), so for the most part the Commodore on the quarter-deck gets his atmosphere at second hand from the sailors on the forecastle. He thinks he breathes it first; but not so. +In much the same way do the commonalty lead their leaders in many other things, at the same time that the leaders little suspect it. +But wherefore it was that after having repeatedly smelt the sea as a merchant sailor, I should now take it into my head to go on a whaling voyage; this the invisible police officer of the Fates, who has the constant surveillance of me, and secretly dogs me, and influences me in some unaccountable way—he can better answer than any one else. +And, doubtless, my going on this whaling voyage, formed part of the grand programme of Providence that was drawn up a long time ago. +It came in as a sort of brief interlude and solo between more extensive performances. +I take it that this part of the bill must have run something like this: +“_Grand Contested Election for the Presidency of the United States._ +“WHALING VOYAGE BY ONE ISHMAEL. “BLOODY BATTLE IN AFFGHANISTAN.” +Amy followed, but she poked her hands out stiffly before her, and jerked herself along as if she went by machinery, and her "Ow!" was more suggestive of pins being run into her than of fear and anguish. +Jo gave a despairing groan, and Meg laughed outright, while Beth let her bread burn as she watched the fun with interest. +"It's no use! Do the best you can when the time comes, and if the audience laughs, don't blame me. Come on, Meg." +Then things went smoothly, for Don Pedro defied the world in a speech of two pages without a single break. Hagar, the witch, chanted an awful incantation over her kettleful of simmering toads, with weird effect. +Roderigo rent his chains asunder manfully, and Hugo died in agonies of remorse and arsenic, with a wild, "Ha! Ha!" +This text is included to make sure Unicode is handled properly: 力加勝北区ᴵᴺᵀᵃছজটডণত +This sample text is public domain and was randomly selected from Project Guttenberg. +The rain had only ceased with the gray streaks of morning at Blazing Star, and the settlement awoke to a moral sense of cleanliness, and the finding of forgotten knives, tin cups, and smaller camp utensils, where the heavy showers had washed away the debris and dust heaps before the cabin doors. +Indeed, it was recorded in Blazing Star that a fortunate early riser had once picked up on the highway a solid chunk of gold quartz which the rain had freed from its incumbering soil, and washed into immediate and glittering popularity. +Possibly this may have been the reason why early risers in that locality, during the rainy season, adopted a thoughtful habit of body, and seldom lifted their eyes to the rifted or india-ink washed skies above them. +"Cass" Beard had risen early that morning, but not with a view to discovery. +A leak in his cabin roof,--quite consistent with his careless, improvident habits,--had roused him at 4 A. M., with a flooded "bunk" and wet blankets. +The chips from his wood pile refused to kindle a fire to dry his bed-clothes, and he had recourse to a more provident neighbor's to supply the deficiency. +This was nearly opposite. +Mr. Cassius crossed the highway, and stopped suddenly. +Something glittered in the nearest red pool before him. +I had one experience with Master Philip before our visitors betook themselves back to Kent, which, unfortunate as it was, I cannot but relate here. My cousin would enter into none of those rough amusements in which I passed my time, for fear, I took it, of spoiling his fine broadcloths or of losing a gold buckle. He never could be got to wrestle, though I challenged him more than once. And he was a well-built lad, and might, with a little practice, have become skilled in that sport. He laughed at the homespun I wore about the farm, saying it was no costume for a gentleman's son, and begged me sneeringly to don leather breeches. He would have none of the company of those lads with whom I found pleasure, young Harvey, and Willis's son, who was being trained as Mr. Starkie's assistant. Nor indeed did I disdain to join in a game with Hugo, who had been given to me, and other negro lads. Philip saw no sport in a wrestle or a fight between two of the boys from the quarters, and marvelled that I could lower myself to bet with Harvey the younger. He took not a spark of interest in the gaming cocks we raised together to compete at the local contests and at the fair, and knew not a gaff from a cockspur. Being one day at my wits' end to amuse my cousin, I proposed to him a game of quoits on the green beside the spring-house, and thither we repaired, followed by Hugo, and young Harvey come to look on. Master Philip, not casting as well as he might, cries out suddenly to Hugo: "Begone, you black dog! What business have you here watching a game between gentlemen?" +But, wonderful to relate, not an irregular, shapeless fragment of crude ore, fresh from Nature's crucible, but a bit of jeweler's handicraft in the form of a plain gold ring. +Looking at it more attentively, he saw that it bore the inscription, "May to Cass." +Like most of his fellow gold-seekers, Cass was superstitious. +The fountain of classic wisdom, Hypatia herself. +As the ancient sage--the name is unimportant to a monk--pumped water nightly that he might study by day, so I, the guardian of cloaks and parasols, at the sacred doors of her lecture-room, imbibe celestial knowledge. +From my youth I felt in me a soul above the matter-entangled herd. +She revealed to me the glorious fact, that I am a spark of Divinity itself. +A fallen star, I am, sir!' continued he, pensively, stroking his lean stomach--'a fallen star!--fallen, if the dignity of philosophy will allow of the simile, among the hogs of the lower world--indeed, even into the hog-bucket itself. Well, after all, I will show you the way to the Archbishop's. +There is a philosophic pleasure in opening one's treasures to the modest young. +Perhaps you will assist me by carrying this basket of fruit?' And the little man jumped up, put his basket on Philammon's head, and trotted off up a neighbouring street. +Philammon followed, half contemptuous, half wondering at what this philosophy might be, which could feed the self-conceit of anything so abject as his ragged little apish guide; +but the novel roar and whirl of the street, the perpetual stream of busy faces, the line of curricles, palanquins, laden asses, camels, elephants, which met and passed him, and squeezed him up steps and into doorways, as they threaded their way through the great Moon-gate into the ample street beyond, drove everything from his mind but wondering curiosity, and a vague, helpless dread of that great living wilderness, more terrible than any dead wilderness of sand which he had left behind. +Already he longed for the repose, the silence of the Laura--for faces which knew him and smiled upon him; but it was too late to turn back now. +His guide held on for more than a mile up the great main street, crossed in the centre of the city, at right angles, by one equally magnificent, at each end of which, miles away, appeared, dim and distant over the heads of the living stream of passengers, the yellow sand-hills of the desert; +while at the end of the vista in front of them gleamed the blue harbour, through a network of countless masts. +and there burst on Philammon's astonished eyes a vast semicircle of blue sea, ringed with palaces and towers. +He stopped involuntarily; and his little guide stopped also, and looked askance at the young monk, to watch the effect which that grand panorama should produce on him. +Nana also troubled him in another way. He had sometimes a feeling that she did not admire him. “I know she admires you tremendously, George,” +Mrs. Darling would assure him, and then she would sign to the children to be specially nice to father. Lovely dances followed, in which the only other servant, Liza, was sometimes allowed to join. +Such a midget she looked in her long skirt and maid's cap, though she had sworn, when engaged, that she would never see ten again. +In the Year 1676, the Prince of _Orange_ having, in concert with the _Spaniards_, resolv'd upon the important Siege of _Maestrich_ (the only Town in the _Dutch_ Provinces, then remaining in the Hands of the _French_) it was accordingly invested about the middle of _June_, with an Army of twenty Thousand Men, under the Command of his Highness Prince +_Waldeck_, with the grand Army covering the Siege. It was some Time before the heavy Cannon, which we expected up the _Maes_, from _Holland_, arrived; which gave Occasion to a Piece of Raillery of Monsieur _Calvo_, the Governor, which was as handsomely repartec'd. That Governor, by a Messenger, intimating his Sorrow to find, we had pawn'd our Cannon for Ammunition Bread. Answer was made, That in a few Days we hoped to give him a Taste of the Loaves, which he should find would be sent him into the Town in extraordinary plenty. I remember another Piece of Raillery, which pass'd some Days after between the _Rhingrave_ and the same _Calvo_. The former sending Word, that he hoped within three Weeks to salute that Governor's Mistress within the Place. _Calvo_ reply'd, He'd give him leave to kiss her all over, if he kiss'd her anywhere in three Months. +And gayest of all was Mrs. Darling, who would pirouette so wildly that all you could see of her was the kiss, and then if you had dashed at her you might have got it. +There never was a simpler happier family until the coming of Peter Pan. +Finally, I always go to sea as a sailor, because of the wholesome exercise and pure air of the fore-castle deck. +For as in this world, head winds are far more prevalent than winds from astern (that is, if you never violate the Pythagorean maxim), so for the most part the Commodore on the quarter-deck gets his atmosphere at second hand from the sailors on the forecastle. He thinks he breathes it first; but not so. +In much the same way do the commonalty lead their leaders in many other things, at the same time that the leaders little suspect it. +But wherefore it was that after having repeatedly smelt the sea as a merchant sailor, I should now take it into my head to go on a whaling voyage; this the invisible police officer of the Fates, who has the constant surveillance of me, and secretly dogs me, and influences me in some unaccountable way—he can better answer than any one else. +And, doubtless, my going on this whaling voyage, formed part of the grand programme of Providence that was drawn up a long time ago. +It came in as a sort of brief interlude and solo between more extensive performances. +The British Isles have been ringing for the last few years with the word 'Art' in its German sense; with 'High Art,' 'Symbolic Art,' 'Ecclesiastical Art,' 'Dramatic Art,' 'Tragic Art,' and so forth; and every well-educated person is expected, nowadays, to know something about Art. Yet in spite of all translations of German 'AEsthetic' treatises, and 'Kunstnovellen,' the mass of the British people cares very little about the matter, and sits contented under the imputation of 'bad taste.' Our stage, long since dead, does not revive; our poetry is dying; our music, like our architecture, only reproduces the past; our painting is only first-rate when it handles landscapes and animals, and seems likely so to remain; but, meanwhile, nobody cares. Some of the deepest and most earnest minds vote the question, in general, a 'sham and a snare,' and whisper to each other +confidentially, that Gothic art is beginning to be a 'bore,' and that Sir Christopher Wren was a very good fellow after all; while the middle classes look on the Art movement half amused, as with a pretty toy, half sulkily suspicious of Popery and Paganism, and think, +apparently, that Art is very well when it means nothing, and is merely used to beautify drawing-rooms and shawl patterns; not to mention that, if there were no painters, Mr. Smith could not hand down to posterity likenesses of himself, Mrs. Smith, and family. But +when 'Art' dares to be in earnest, and to mean something, much more to connect itself with religion, Smith's tone alters. He will teach 'Art' to keep in what he considers its place, and if it refuses, take the law of it, and put it into the Ecclesiastical Court. So he says, and what is more, he means what he says; and as all the world, from Hindostan to Canada, knows by most practical proof, what he means, he sooner or later does, perhaps not always in the wisest way, but still he does it.Ah! It's pleasant to drop into my own easy-chair my dear though a little palpitating what with trotting up-stairs and what with trotting down, and why kitchen stairs should all be corner stairs is for the builders to justify though I do not think they fully understand their trade and never did, else why the sameness and why not more conveniences and fewer +draughts and likewise making a practice of laying the plaster on too thick I am well convinced which holds the damp, and as to chimney-pots putting them on by guess-work like hats at a party and no more knowing what their effect will be upon the smoke bless you than I do if so much, except that it will mostly be either to send it down your throat in a straight form or give it a twist before it goes there. And what I says speaking as I find of those new metal chimneys all manner of shapes (there's a row of 'em at Miss Wozenham's lodging-house lower down on the other side of the way) is that they only work your smoke into artificial patterns for you before you swallow it and that I'd quite as soon swallow mine plain, the flavour being the same, not to mention the conceit of putting up signs on the top of your house to show the forms in which you take your smoke into your inside +Amy followed, but she poked her hands out stiffly before her, and jerked herself along as if she went by machinery, and her "Ow!" was more suggestive of pins being run into her than of fear and anguish. +Jo gave a despairing groan, and Meg laughed outright, while Beth let her bread burn as she watched the fun with interest. +"It's no use! Do the best you can when the time comes, and if the audience laughs, don't blame me. Come on, Meg." +Then things went smoothly, for Don Pedro defied the world in a speech of two pages without a single break. Hagar, the witch, chanted an awful incantation over her kettleful of simmering toads, with weird effect.''' \ No newline at end of file diff --git a/python/cudf/cudf/tests/data/vocab_hash/bert-base-uncased-vocab-5per.txt b/python/cudf/cudf/tests/data/vocab_hash/bert-base-uncased-vocab-5per.txt deleted file mode 100644 index 7d156a38f28..00000000000 --- a/python/cudf/cudf/tests/data/vocab_hash/bert-base-uncased-vocab-5per.txt +++ /dev/null @@ -1,2475 +0,0 @@ -[PAD] -[unused0] -[unused1] -[unused2] -[unused3] -[unused4] -[unused5] -[unused6] -[unused7] -[unused8] -[unused9] -[unused10] -[unused11] -[unused12] -[unused13] -[unused14] -[unused15] -[unused16] -[unused17] -[unused18] -[unused19] -[unused20] -[unused21] -[unused22] -[unused23] -[unused24] -[unused25] -[unused26] -[unused27] -[unused28] -[unused29] -[unused30] -[unused31] -[unused32] -[unused33] -[unused34] -[unused35] -[unused36] -[unused37] -[unused38] -[unused39] -[unused40] -[unused41] -[unused42] -[unused43] -[unused44] -[unused45] -[unused46] -[unused47] -[unused48] -[unused49] -[unused50] -[unused51] -[unused52] -[unused53] -[unused54] -[unused55] -[unused56] -[unused57] -[unused58] -[unused59] -[unused60] -[unused61] -[unused62] -[unused63] -[unused64] -[unused65] -[unused66] -[unused67] -[unused68] -[unused69] -[unused70] -[unused71] -[unused72] -[unused73] -[unused74] -[unused75] -[unused76] -[unused77] -[unused78] -[unused79] -[unused80] -[unused81] -[unused82] -[unused83] -[unused84] -[unused85] -[unused86] -[unused87] -[unused88] -[unused89] -[unused90] -[unused91] -[unused92] -[unused93] -[unused94] -[unused95] -[unused96] -[unused97] -[unused98] -[UNK] -[CLS] -[SEP] -[MASK] -[unused99] -[unused100] -[unused101] -[unused102] -[unused103] -[unused104] -[unused105] -[unused106] -[unused107] -[unused108] -[unused109] -[unused110] -[unused111] -[unused112] -[unused113] -[unused114] -[unused115] -[unused116] -[unused117] -[unused118] -[unused119] -[unused120] -[unused121] -[unused122] -[unused123] -[unused124] -[unused125] -[unused126] -[unused127] -[unused128] -[unused129] -[unused130] -[unused131] -[unused132] -[unused133] -[unused134] -[unused135] -[unused136] -[unused137] -[unused138] -[unused139] -[unused140] -[unused141] -[unused142] -[unused143] -[unused144] -[unused145] -[unused146] -[unused147] -[unused148] -[unused149] -[unused150] -[unused151] -[unused152] -[unused153] -[unused154] -[unused155] -[unused156] -[unused157] -[unused158] -[unused159] -[unused160] -[unused161] -[unused162] -[unused163] -[unused164] -[unused165] -[unused166] -[unused167] -[unused168] -[unused169] -[unused170] -[unused171] -[unused172] -[unused173] -[unused174] -[unused175] -[unused176] -[unused177] -[unused178] -[unused179] -[unused180] -[unused181] -[unused182] -[unused183] -[unused184] -[unused185] -[unused186] -[unused187] -[unused188] -[unused189] -[unused190] -[unused191] -[unused192] -[unused193] -[unused194] -[unused195] -[unused196] -[unused197] -[unused198] -[unused199] -[unused200] -[unused201] -[unused202] -[unused203] -[unused204] -[unused205] -[unused206] -[unused207] -[unused208] -[unused209] -[unused210] -[unused211] -[unused212] -[unused213] -[unused214] -[unused215] -[unused216] -[unused217] -[unused218] -[unused219] -[unused220] -[unused221] -[unused222] -[unused223] -[unused224] -[unused225] -[unused226] -[unused227] -[unused228] -[unused229] -[unused230] -[unused231] -[unused232] -[unused233] -[unused234] -[unused235] -[unused236] -[unused237] -[unused238] -[unused239] -[unused240] -[unused241] -[unused242] -[unused243] -[unused244] -[unused245] -[unused246] -[unused247] -[unused248] -[unused249] -[unused250] -[unused251] -[unused252] -[unused253] -[unused254] -[unused255] -[unused256] -[unused257] -[unused258] -[unused259] -[unused260] -[unused261] -[unused262] -[unused263] -[unused264] -[unused265] -[unused266] -[unused267] -[unused268] -[unused269] -[unused270] -[unused271] -[unused272] -[unused273] -[unused274] -[unused275] -[unused276] -[unused277] -[unused278] -[unused279] -[unused280] -[unused281] -[unused282] -[unused283] -[unused284] -[unused285] -[unused286] -[unused287] -[unused288] -[unused289] -[unused290] -[unused291] -[unused292] -[unused293] -[unused294] -[unused295] -[unused296] -[unused297] -[unused298] -[unused299] -[unused300] -[unused301] -[unused302] -[unused303] -[unused304] -[unused305] -[unused306] -[unused307] -[unused308] -[unused309] -[unused310] -[unused311] -[unused312] -[unused313] -[unused314] -[unused315] -[unused316] -[unused317] -[unused318] -[unused319] -[unused320] -[unused321] -[unused322] -[unused323] -[unused324] -[unused325] -[unused326] -[unused327] -[unused328] -[unused329] -[unused330] -[unused331] -[unused332] -[unused333] -[unused334] -[unused335] -[unused336] -[unused337] -[unused338] -[unused339] -[unused340] -[unused341] -[unused342] -[unused343] -[unused344] -[unused345] -[unused346] -[unused347] -[unused348] -[unused349] -[unused350] -[unused351] -[unused352] -[unused353] -[unused354] -[unused355] -[unused356] -[unused357] -[unused358] -[unused359] -[unused360] -[unused361] -[unused362] -[unused363] -[unused364] -[unused365] -[unused366] -[unused367] -[unused368] -[unused369] -[unused370] -[unused371] -[unused372] -[unused373] -[unused374] -[unused375] -[unused376] -[unused377] -[unused378] -[unused379] -[unused380] -[unused381] -[unused382] -[unused383] -[unused384] -[unused385] -[unused386] -[unused387] -[unused388] -[unused389] -[unused390] -[unused391] -[unused392] -[unused393] -[unused394] -[unused395] -[unused396] -[unused397] -[unused398] -[unused399] -[unused400] -[unused401] -[unused402] -[unused403] -[unused404] -[unused405] -[unused406] -[unused407] -[unused408] -[unused409] -[unused410] -[unused411] -[unused412] -[unused413] -[unused414] -[unused415] -[unused416] -[unused417] -[unused418] -[unused419] -[unused420] -[unused421] -[unused422] -[unused423] -[unused424] -[unused425] -[unused426] -[unused427] -[unused428] -[unused429] -[unused430] -[unused431] -[unused432] -[unused433] -[unused434] -[unused435] -[unused436] -[unused437] -[unused438] -[unused439] -[unused440] -[unused441] -[unused442] -[unused443] -[unused444] -[unused445] -[unused446] -[unused447] -[unused448] -[unused449] -[unused450] -[unused451] -[unused452] -[unused453] -[unused454] -[unused455] -[unused456] -[unused457] -[unused458] -[unused459] -[unused460] -[unused461] -[unused462] -[unused463] -[unused464] -[unused465] -[unused466] -[unused467] -[unused468] -[unused469] -[unused470] -[unused471] -[unused472] -[unused473] -[unused474] -[unused475] -[unused476] -[unused477] -[unused478] -[unused479] -[unused480] -[unused481] -[unused482] -[unused483] -[unused484] -[unused485] -[unused486] -[unused487] -[unused488] -[unused489] -[unused490] -[unused491] -[unused492] -[unused493] -[unused494] -[unused495] -[unused496] -[unused497] -[unused498] -[unused499] -[unused500] -[unused501] -[unused502] -[unused503] -[unused504] -[unused505] -[unused506] -[unused507] -[unused508] -[unused509] -[unused510] -[unused511] -[unused512] -[unused513] -[unused514] -[unused515] -[unused516] -[unused517] -[unused518] -[unused519] -[unused520] -[unused521] -[unused522] -[unused523] -[unused524] -[unused525] -[unused526] -[unused527] -[unused528] -[unused529] -[unused530] -[unused531] -[unused532] -[unused533] -[unused534] -[unused535] -[unused536] -[unused537] -[unused538] -[unused539] -[unused540] -[unused541] -[unused542] -[unused543] -[unused544] -[unused545] -[unused546] -[unused547] -[unused548] -[unused549] -[unused550] -[unused551] -[unused552] -[unused553] -[unused554] -[unused555] -[unused556] -[unused557] -[unused558] -[unused559] -[unused560] -[unused561] -[unused562] -[unused563] -[unused564] -[unused565] -[unused566] -[unused567] -[unused568] -[unused569] -[unused570] -[unused571] -[unused572] -[unused573] -[unused574] -[unused575] -[unused576] -[unused577] -[unused578] -[unused579] -[unused580] -[unused581] -[unused582] -[unused583] -[unused584] -[unused585] -[unused586] -[unused587] -[unused588] -[unused589] -[unused590] -[unused591] -[unused592] -[unused593] -[unused594] -[unused595] -[unused596] -[unused597] -[unused598] -[unused599] -[unused600] -[unused601] -[unused602] -[unused603] -[unused604] -[unused605] -[unused606] -[unused607] -[unused608] -[unused609] -[unused610] -[unused611] -[unused612] -[unused613] -[unused614] -[unused615] -[unused616] -[unused617] -[unused618] -[unused619] -[unused620] -[unused621] -[unused622] -[unused623] -[unused624] -[unused625] -[unused626] -[unused627] -[unused628] -[unused629] -[unused630] -[unused631] -[unused632] -[unused633] -[unused634] -[unused635] -[unused636] -[unused637] -[unused638] -[unused639] -[unused640] -[unused641] -[unused642] -[unused643] -[unused644] -[unused645] -[unused646] -[unused647] -[unused648] -[unused649] -[unused650] -[unused651] -[unused652] -[unused653] -[unused654] -[unused655] -[unused656] -[unused657] -[unused658] -[unused659] -[unused660] -[unused661] -[unused662] -[unused663] -[unused664] -[unused665] -[unused666] -[unused667] -[unused668] -[unused669] -[unused670] -[unused671] -[unused672] -[unused673] -[unused674] -[unused675] -[unused676] -[unused677] -[unused678] -[unused679] -[unused680] -[unused681] -[unused682] -[unused683] -[unused684] -[unused685] -[unused686] -[unused687] -[unused688] -[unused689] -[unused690] -[unused691] -[unused692] -[unused693] -[unused694] -[unused695] -[unused696] -[unused697] -[unused698] -[unused699] -[unused700] -[unused701] -[unused702] -[unused703] -[unused704] -[unused705] -[unused706] -[unused707] -[unused708] -[unused709] -[unused710] -[unused711] -[unused712] -[unused713] -[unused714] -[unused715] -[unused716] -[unused717] -[unused718] -[unused719] -[unused720] -[unused721] -[unused722] -[unused723] -[unused724] -[unused725] -[unused726] -[unused727] -[unused728] -[unused729] -[unused730] -[unused731] -[unused732] -[unused733] -[unused734] -[unused735] -[unused736] -[unused737] -[unused738] -[unused739] -[unused740] -[unused741] -[unused742] -[unused743] -[unused744] -[unused745] -[unused746] -[unused747] -[unused748] -[unused749] -[unused750] -[unused751] -[unused752] -[unused753] -[unused754] -[unused755] -[unused756] -[unused757] -[unused758] -[unused759] -[unused760] -[unused761] -[unused762] -[unused763] -[unused764] -[unused765] -[unused766] -[unused767] -[unused768] -[unused769] -[unused770] -[unused771] -[unused772] -[unused773] -[unused774] -[unused775] -[unused776] -[unused777] -[unused778] -[unused779] -[unused780] -[unused781] -[unused782] -[unused783] -[unused784] -[unused785] -[unused786] -[unused787] -[unused788] -[unused789] -[unused790] -[unused791] -[unused792] -[unused793] -[unused794] -[unused795] -[unused796] -[unused797] -[unused798] -[unused799] -[unused800] -[unused801] -[unused802] -[unused803] -[unused804] -[unused805] -[unused806] -[unused807] -[unused808] -[unused809] -[unused810] -[unused811] -[unused812] -[unused813] -[unused814] -[unused815] -[unused816] -[unused817] -[unused818] -[unused819] -[unused820] -[unused821] -[unused822] -[unused823] -[unused824] -[unused825] -[unused826] -[unused827] -[unused828] -[unused829] -[unused830] -[unused831] -[unused832] -[unused833] -[unused834] -[unused835] -[unused836] -[unused837] -[unused838] -[unused839] -[unused840] -[unused841] -[unused842] -[unused843] -[unused844] -[unused845] -[unused846] -[unused847] -[unused848] -[unused849] -[unused850] -[unused851] -[unused852] -[unused853] -[unused854] -[unused855] -[unused856] -[unused857] -[unused858] -[unused859] -[unused860] -[unused861] -[unused862] -[unused863] -[unused864] -[unused865] -[unused866] -[unused867] -[unused868] -[unused869] -[unused870] -[unused871] -[unused872] -[unused873] -[unused874] -[unused875] -[unused876] -[unused877] -[unused878] -[unused879] -[unused880] -[unused881] -[unused882] -[unused883] -[unused884] -[unused885] -[unused886] -[unused887] -[unused888] -[unused889] -[unused890] -[unused891] -[unused892] -[unused893] -[unused894] -[unused895] -[unused896] -[unused897] -[unused898] -[unused899] -[unused900] -[unused901] -[unused902] -[unused903] -[unused904] -[unused905] -[unused906] -[unused907] -[unused908] -[unused909] -[unused910] -[unused911] -[unused912] -[unused913] -[unused914] -[unused915] -[unused916] -[unused917] -[unused918] -[unused919] -[unused920] -[unused921] -[unused922] -[unused923] -[unused924] -[unused925] -[unused926] -[unused927] -[unused928] -[unused929] -[unused930] -[unused931] -[unused932] -[unused933] -[unused934] -[unused935] -[unused936] -[unused937] -[unused938] -[unused939] -[unused940] -[unused941] -[unused942] -[unused943] -[unused944] -[unused945] -[unused946] -[unused947] -[unused948] -[unused949] -[unused950] -[unused951] -[unused952] -[unused953] -[unused954] -[unused955] -[unused956] -[unused957] -[unused958] -[unused959] -[unused960] -[unused961] -[unused962] -[unused963] -[unused964] -[unused965] -[unused966] -[unused967] -[unused968] -[unused969] -[unused970] -[unused971] -[unused972] -[unused973] -[unused974] -[unused975] -[unused976] -[unused977] -[unused978] -[unused979] -[unused980] -[unused981] -[unused982] -[unused983] -[unused984] -[unused985] -[unused986] -[unused987] -[unused988] -[unused989] -[unused990] -[unused991] -[unused992] -[unused993] -notions -bc -宀 -rudd -1839 -appeals -fabric -charges -roadside -interview -goethe -rr -##ス -heroine -housing -##xx -owens -who -青 -prominent -##ments -spare -sloane -paranormal -昭 -physiological -reply -josiah -mort -316 -descend -pencil -nepal -minus -janata -106 -hardin -enamel -ђ -##urance -scans -happening -advising -identifies -institutional -dominican -charts -convince -51st -kingdoms -heritage -comedy -banker -##me -##its -1611 -##dos -##pire -fog -bulky -light -reborn -consultation -##loading -teachers -ashe -frank -bandits -governmental -##rdon -privileged -##mins -##ᅭ -adherence -ko -motionless -fighters -sui -significant -others -incurred -posts -defected -alexandra -continue -tormented -encourage -delaying -stomach -conscience -deserve -pumps -tanaka -##fles -flushed -parks -##nam -##hold -submitted -duran -monroe -parasites -emptiness -prakash -break -##xley -haynes -neil -gallons -##onus -manages -69 -aragon -memorable -matching -inclination -molten -humboldt -riders -##ː -marketed -##yala -squealed -##qual -talk -tournaments -##boot -##a -##gart -##rra -##ifies -deny -personal -nancy -splits -##ˈ -permitting -projected -fool -attach -pill -1776 -##ს -raoul -cocaine -conservation -luther -appeal -##tal -dq -##gny -##vial -reasonable -unitarian -melville -stephens -##民 -balancing -lowe -swore -matrix -concluded -##cap -tee -capacity -searing -living -lc -qualified -shoes -##sam -rounds -293 -237 -attic -shafts -older -underworld -tehran -simulcast -##abe -##lich -quarterfinals -elton -unreasonable -ষ -m1 -upbringing -faith -misconduct -##ᄀ -miner -sudanese -dense -whimpered -sabre -professionally -ex -grasp -infections -knee -shoving -ł -##ehan -##ʊ -skyscraper -##mont -purchases -fright -drivers -buyer -shit -foreigner -1764 -gloria -##rem -rejecting -petra -spots -memoirs -tracey -##gy -##ades -reused -##igh -3 -hopeless -rita -specialised -〈 -portion -distress -##ו -##tub -jamestown -clause -journalists -network -napoleon -##դ -fortunate -focus -ahead -concordia -168 -hitch -managed -superliga -shu -euroleague -clock -johnny -merits -softened -grimaced -losses -ferdinand -gmbh -wreath -##astic -harlem -den -algae -43rd -##kushima -welding -abilities -##nting -assumption -announcer -auditioned -mathematician -retrospective -##谷 -laced -motive -fixed -joshua -emerging -foreign -brutal -れ -seaside -manfred -connects -##nesian -heir -ս -experience -jem -##sett -mark -harper -##de -one -additive -bypass -assembly -continents -boom -minimal -##wheel -unification -participating -shareholder -arizona -casimir -##স -viet -##inski -restoring -abolished -dea -glowed -aeronautics -selfish -serbia -easily -count -##llet -justified -descendant -racer -authentic -##care -weightlifting -annapolis -songs -abrams -deciduous -costing -silence -spent -##form -##kou -screens -braden -##scribe -1934 -permit -##ric -##ane -gaston -comet -gift -trembled -##fa -everyday -forged -milwaukee -##« -larger -nickel -danes -appropriated -malibu -banquet -shared -26 -##ucher -ky -##urse -ᅴ -divided -##itarian -endeavors -blank -kw -investor -##ners -moldova -veteran -pandit -deposited -involved -pace -experiencing -£ -algorithms -safely -##pore -xp -unite -fin -clearance -intriguing -indications -almighty -##iable -corvette -##uding -panzer -monster -ban -eternal -trap -shot -saxophonist -##iam -dai -studio -##eak -baltic -placed -northern -suppliers -fungus -prasad -##qu -priorities -robinson -list -conner -1960 -hendricks -##ani -shoulder -kite -starting -remorse -mysteriously -##master -lena -1923 -##〜 -##ln -pianist -##ision -adulthood -century -skier -cannot -mai -summarized -oxfordshire -##dded -ordination -##vis -tin -resource -expanse -inmate -doyle -##osa -stained -yemen -spoken -stealth -natalie -neatly -stamp -pens -beautiful -ک -pulp -shortlisted -anastasia -models -theory -coincided -##dor -deception -##rt -comparable -##न -offered -##ק -indonesian -canberra -martinez -whereas -clutch -aqua -roadway -dorothea -triumph -landfall -ernesto -##quist -מ -turk -##bau -##num -teammate -injuring -vampire -##լ -somewhat -sophia -standards -##♦ -actions -judas -electoral -robertson -crap -depictions -inherited -##ffey -byron -demands -gorilla -##mona -misunderstood -dante -##yah -##ark -compiled -gaulle -win -activity -giggling -polynomials -plantation -included -ɛ -kai -282 -vol -1650 -nationally -stick -friar -bourbon -vow -erratic -drifting -pornography -क -remains -##ನ -dumping -rifle -backdrop -found -pondered -gil -##hetic -outlaw -secure -razor -survived -behind -objectives -josef -##空 -##typical -intuitive -within -##秀 -torch -relevant -scoop -sox -buckle -motion -##gging -chorale -genetically -##ར -##· -yell -yielding -##riam -campbell -archive -hillsborough -diamond -somerville -glitter -graduating -##॥ -kendall -天 -videos -disdain -nigerian -##esses -##hoe -jillian -bloomberg -##miya -fleeting -quilt -facebook -witnessed -destruction -cleansing -christophe -explained -mistaken -##toy -college -##arus -islanders -releasing -tourist -psychotic -thrilled -alt -rafael -wessex -pastry -enthusiast -assists -##ress -出 -##aker -eu -specializing -##borg -##if -unofficially -studied -locating -cello -hawks -chevalier -182 -tissues -cactus -onslaught -##kur -purely -清 -invited -##χ -joey -##ered -##uin -##brook -club -billion -culturally -##hale -##hot -##quisite -riches -ghosts -definite -fern -boycott -worse -arrange -##西 -##br -timber -eva -might -truss -nude -r -envisioned -1635 -dependence -##ault -##lding -##nh -eyelashes -##agi -spun -##vich -grouped -problem -helium -##dge -dent -##rick -mustache -progress -cuthbert -##鈴 -hurling -1779 -shepherd -pd -tiffany -pad -modules -scientology -rapper -electors -colorado -enforcement -##inate -##rent -murderer -boise -pollard -mason -drafting -straightforward -accompanying -extensions -scaled -yamamoto -. -facilitate -bombardment -101 -inviting -##mute -198 -dammit -layne -dubai -clint -shelves -briggs -##istan -revised -remembers -soloist -gables -cranes -##ower -##ok -kaladin -1961 -abdul -weiss -startup -thereof -hose -unofficial -##berg -knicks -enroll -das -satisfactory -playwright -functional -##oted -exposure -credited -国 -returned -searched -wii -banning -champaign -asthma -##son -##yk -elias -##ᵉ -directorial -##vos -swing -karin -fours -cbc -##bers -residences -exploration -depending -credentials -sykes -а -##マ -restarted -##nivorous -357 -attacks -trumpets -combinations -banking -stops -##listic -maneuvers -duffy -mutation -stop -explorers -behaviour -subtly -developing -ions -responsibility -racks -colombo -widowed -addison -ironic -ventured -utc -##ゆ -eh -volvo -extinguished -248 -##ク -enclosure -impulse -shocks -rainforest -waiter -##月 -circumstances -windmill -cambrian -granada -nichols -ordained -terraces -renewed -geese -cite -##oc -anticipating -eccentric -macau -jing -##itz -spike -stunt -democratic -talmud -flooded -blew -tate -sharks -##ali -##ロ -blanc -defense -driven -snapped -halfway -whispering -##mist -debuted -##good -legion -sun -alias -##lier -rigid -amounted -mari -relates -allowed -negotiations -rosary -investigators -gossip -transcribed -broker -nocturnal -《 -reckon -##ट -##kla -wil -amid -lincolnshire -н -mister -remarked -##yd -##hardt -##escence -##を -soothe -accounted -pavel -nonstop -baronetcy -##irs -##aly -preparing -specializes -##ena -strategies -steelers -語 -145 -madness -##ays -stresses -isolate -uranium -vu -collections -cabbage -bremen -jews -trailers -森 -##eg -protectorate -174 -collapsing -止 -offer -302 -ached -degrees -421 -physiology -silk -toward -eventual -##court -talents -##ular -cheng -parameters -surge -fifties -##ز -proclaimed -billing -watery -handbook -compositions -##nished -piano -ministries -⽥ -arterial -##nous -##th -publishes -integration -hotter -decision -souza -pitchfork -started -hezbollah -##s -vietnamese -initiated -ubiquitous -と -af -electrical -bend -minnesota -pigeon -cardiovascular -##ol -sparrow -readers -wilkinson -bled -hugo -brute -1811 -dust -distinguishing -##lett -goldstein -pontiac -detainees -bautista -94 -##wald -##45 -年 -blowing -bartholomew -##dit -airplane -projection -##furt -lighting -##ke -1830s -cantata -tumbling -iraqi -shape -mortals -albans -shannon -warlock -ventral -smug -quoting -buy -overboard -kelley -##lifting -order -##50 -foothills -lahore -##ctus -refugee -##ion -deepest -wetland -telugu -น -clears -hiring -freighter -throbbing -attacked -##lian -haiti -repeal -##れ -##59 -paddy -bladder -beating -soviets -pit -blackmail -##ifier -timed -advances -##tment -gesellschaft -ronan -newtown -niger -neon -##boards -barbarians -barrie -longitude -##cio -immigrant -annum -sprint -trumpeter -1956 -uprising -##ও -sustain -interchange -blazed -motif -sediment -absorbing -wah -jun -艹 -310 -##miento -##nas -venture -##idal -extend -locker -constraint -newport -identifying -arctic -##ہ -cary -handkerchief -##ٹ -upright -nouns -म -admitting -contested -duke -healing -##29 -photographers -equestrian -thrusts -##fi -249 -propose -balloons -interfering -terrifying -leinster -sweeps -ₕ -##hi -accuracy -trunk -bundesliga -slug -handled -stance -vibrations -northbound -gamma -##oor -cigar -buster -thirds -loch -##nessy -detector -nascar -terminated -321 -chooses -horizontal -genesis -##urs -##bility -listen -45th -worker -mc -mexican -kendrick -info -##uw -pediatric -rev -isolated -observation -##nified -orient -delicate -breakers -##chet -1662 -hovered -##nl -beck -75 -##tama -hawaii -wrestler -singers -tire -suspected -measurement -ᄊ -sprawling -gordon -excess -support -kind -commemorated -skilled -insertion -consider -mustered -covent -##mad -##vision -##ria -arsenic -ನ -policemen -enrollment -ios -graveyard -peruvian -unemployment -regulations -above -civilians -cents -worry -townspeople -forced -##uru -##sz -cause -sighs -compiler -sable -fact -surgery -dc -##pants -##beat -sings -##field -recall -cape -eyeing -hana -bullshit -baird -navigator -netting -squire -delayed -##nai -cretaceous -medina -presided -lengths -##trix -schedules -cardiac -heal -brody -lal -entire -crossings -preach -brigade -diva -breaks -boogie -##tre -cycling -goblin -spread -engineers -servants -1000 -posthumous -beyond -jerry -¶ -procession -togo -coyote -organist -##abad -them -##view -app -##alle -interviewed -oblivion -##rosis -hu -talking -530 -bombing -philosopher -escalated -miocene -oyster -donkey -assaults -##graphs -honest -falcons -korean -collaborator -azerbaijan -gunpowder -stuck -demo -embryo -forty -##or -finland -wrenched -confederation -eng -paving -##west -baroque -execute -personnel -lars -##oise -yanked -needy -leah -nicknamed -bestowed -guangzhou -hanson -##bbling -hundred -kilometer -night -##│ -##fields -racehorse -curved -後 -median -guantanamo -superstructure -##isan -##odes -grins -み -##channel -burnley -##pins -auxiliary -caine -white -valuation -##lving -expert -##rea -mauritius -iona -font -valentine -tribes -##bate -profitable -##rogate -ebony -soo -initial -marin -helicopters -debates -dragging -decent -##ours -yourself -microscopy -compilation -intensive -bronx -kimberly -wills -digging -rumored -werner -##fleet -bracing -sloan -album -meaningful -marine -essence -hindi -##dorf -doubt -thirties -habits -mangrove -miracles -ballast -` -bodied -persist -browning -mas -##sl -child -historically -certificates -polished -lucivar -benevolent -tipperary -repealed -centennial -##analysis -middle -difficulty -archived -elk -ha -lamp -barrow -##ध -meteor -##cellular -turkey -藤 -dune -xml -##hered -lo -unbearable -ottoman -licked -talon -ლ -yorker -smashed -damage -landau -visited -##ressed -suit -reactors -gracefully -cleveland -baptiste -martini -unified -ratified -kobe -attract -accelerated -##duk -suffix -builders -infection -wit -##qa -entering -ninety -touching -hue -headline -advises -##ש -sand -rms -##am -paddington -ebook -brightened -td -butter -rayon -bilingual -granite -³ -eden -##vres -distinguish -madman -syntax -rigorous -shortages -##sian -bancroft -praised -administrators -##bolic -##pool -paved -compatible -morrow -thumping -declares -convincing -freelance -differently -accelerating -arrive -##nated -preliminary -wraps -fringe -glow -##tension -stockings -emmanuel -according -carmen -sprayed -pulsed -lyman -angola -styled -sewing -confronting -##idad -bitterness -eating -presley -district -stevie -##ddle -##nel -clutches -carry -##¡ -##para -royals -1936 -rebranded -departing -ᵏ -overrun -legend -kitty -##400 -enchanted -funny -activities diff --git a/python/cudf/cudf/tests/data/vocab_hash/ground_truth_vocab_hash_5per.txt b/python/cudf/cudf/tests/data/vocab_hash/ground_truth_vocab_hash_5per.txt deleted file mode 100644 index b63a3b883a4..00000000000 --- a/python/cudf/cudf/tests/data/vocab_hash/ground_truth_vocab_hash_5per.txt +++ /dev/null @@ -1,3100 +0,0 @@ -26899 -27424 -618 -0 0 -9308113254891556867 0 -436896553784417287 3 -2385682838371229189 10 -1558319825288788995 15 -1694636932519955459 18 -10804654694511444996 21 -18098925775253437954 25 -6737924902096233986 27 -12420164637450604548 29 -10052209762446030853 33 -17136277123483534341 38 -3816962228559380485 43 -10000432285949172226 48 -3595721221582369282 50 -8300629869194307073 52 -13201888020163964420 53 -1911143973647033348 57 -6800828846832400386 61 -11236791597074179585 63 -9168098438593696772 64 -7262274946389325317 68 -2138829751639336965 73 -5569775297965074438 78 -6731361918165622787 84 -1000159591814497798 87 -12420398894488158722 93 -17276977059282736646 95 -7635600731429963779 101 -18378821292746091014 104 -2022931426448033286 110 -6648129782745846792 116 -4965400020463248899 124 -5551261944186869761 127 -11423501398563713540 128 -12015445961309659140 132 -14804906647292574211 136 -6319334893796554242 139 -7402632997775432193 141 -11021449308525840900 142 -7668313675584212486 146 -8416534454442286083 152 -14786755608460337669 155 -10252394770332982789 160 -10551221318314345989 165 -16096303530760742404 170 -9259785140742614530 174 -14075929275811587074 176 -317926420102044679 178 -779174831516893192 185 -1715448660039157764 193 -4021514903658568197 197 -14867534496075520001 202 -12719282834178743811 203 -17465980749405462021 206 -8925774748288079874 211 -7623696658283396611 213 -17929326859744067590 216 -12671154751486019589 222 -12039326031585824261 227 -18347446751167835650 232 -17064637144737108996 234 -11021218191937939462 238 -13156493120251779587 244 -8337641745437302789 247 -7529617071985650692 252 -15855511622382862341 256 -9880671126311259651 261 -4648648374527148548 264 -9643729509462706180 268 -16079254189330675202 272 -5912862662665074690 274 -3064447562379233285 276 -4679971614763188741 281 -5680163499515301381 286 -13723289572021756419 291 -12449253329930581508 294 -4151764428362670084 298 -11785134993959286786 302 -3078863087420124165 304 -15185198810953060869 309 -8297057339576492548 314 -13461860185754054147 318 -3436285402337504774 321 -14202036946604226052 327 -1429187245268482565 331 -1523079254859276295 336 -16225831803604224005 343 -7556943554185476098 348 -17338728822182943748 350 -16838388931654467591 354 -5518924113746022405 361 -16613589795813448195 366 -16245874414877492227 369 -16854419533518505478 372 -2539676113398158854 378 -12042249427062731780 384 -18319457514850241026 388 -4667739140302370305 390 -0 391 -6381331313726406148 391 -3908582673448970756 395 -12826200984566221323 399 -1008697446161023492 410 -1045330962753696770 414 -12519080959524890629 416 -3292967686690497028 421 -9027706222753190402 425 -7627948746545135618 427 -7113204062919493122 429 -1902402424944787974 431 -8582449967256837125 437 -5093494603780084229 442 -15952412032150427656 447 -11024499228689010178 455 -6599137048949621252 457 -12441911088884211715 461 -587330806707068418 464 -6920270191183132164 466 -6534248742955117062 470 -17914513122977299972 476 -18340097295513629191 480 -13676127406031174657 487 -1387609897069507076 488 -1763073162200316424 492 -10502193626894192132 500 -6191089615101093380 504 -3995303291068676098 508 -12574723308968873478 510 -9457112246136620037 516 -2659981631401145858 521 -5678978765742315521 523 -2321007311402405891 524 -7262894069470853636 527 -5310255954615393799 531 -0 538 -4947937557054473220 538 -16886302206430107652 542 -1841567627260397061 546 -1726903440913575428 551 -14879875423439711745 555 -10406819091468369412 556 -11283521635310509569 560 -3680912523998301189 561 -15838266909581394435 566 -12391804375709570050 569 -1020307774365172228 571 -1922720619059759621 575 -10324920589640484359 580 -7346645981525338116 587 -14105351180271546371 591 -18409889360013782019 594 -4787164988982490117 597 -10284218591671275522 602 -13929073926559228929 604 -13709653197282269188 605 -11724057172276600835 609 -866864533352873475 612 -1687533916118276610 615 -14962991064861439492 617 -3075368686919669253 621 -7596099351713916421 626 -9307207198362792453 631 -7250040662483023365 636 -3682040329320065539 641 -793686382876029444 644 -14333782297404873732 648 -1825793630924954627 652 -84619541393612289 655 -13182271411077086213 656 -9374841722325437444 661 -11891971769486715395 665 -8043937648578607618 668 -14046873830950905859 670 -8968303908578660869 673 -12592319890201396232 678 -11282395698657584643 686 -3688562500562735107 689 -4497389441661304322 692 -864575183575292419 694 -1036491259062044675 697 -14213351843646783493 700 -7708978415027076099 705 -6987701493730463747 708 -4532132061628409865 711 -1889838133194080259 720 -14464742354403783685 723 -11471457412392914945 728 -17301684950834749955 729 -2740600303999292421 732 -1861681694596322820 737 -2466512513818280965 741 -567322683033477639 746 -4219982383453752324 753 -4414272813071813121 757 -18296812503650796552 758 -14106238638274939908 766 -8343039090563483140 770 -12899982907471985156 774 -5995542065075040773 778 -1943074365889546244 783 -16642415363908390916 787 -681630493618758662 791 -4461862742627854853 797 -3899665054961662466 802 -9373615873629220356 804 -1672886650488992771 808 -16881710947150074885 811 -5794767402723235334 816 -5402108652823464964 822 -11161133355542430211 826 -17005555433296348161 829 -16516779711980265477 830 -1238971681242829316 835 -7495779865157534723 839 -5869815147311233538 842 -12543930235265506819 844 -16347355750539933194 847 -1618182687158588418 857 -5475339393482339330 859 -1820163941351445507 861 -9781870903757679621 864 -6959962931369883652 869 -6165520526154289668 873 -4608135616958660097 877 -5347147311423033859 878 -4274882894736749062 881 -12469579236131750918 887 -14358338421054221826 893 -14307263809632171012 895 -12240285085875511812 899 -3612936311740236807 903 -15402889954020054020 910 -4862865790365874693 914 -12873671979026176002 919 -5762125948543834631 921 -16549663359069884422 928 -17127464586321114627 934 -5489118657490741761 937 -1439915557249128451 938 -5591921604825754115 941 -12215727902556801029 944 -1292449902890727941 949 -252852670543746566 954 -6662678800904557571 960 -11937079555774719491 963 -9580831801307231749 966 -585888224829458434 971 -2614731491636606978 973 -4039106186911400965 975 -85542184548733958 980 -1232905200495278082 986 -12898496382527018499 988 -4488285801271117316 991 -5603423086377753092 995 -2857864416179998726 999 -100295911725428228 1005 -15911528881864497155 1009 -10785833246735411207 1012 -7176774503550155271 1019 -14860337537539733508 1026 -16041518408725938691 1030 -11338701253034655748 1033 -14943908174444697092 1037 -3242489974158897670 1041 -3082940798033554949 1047 -3158850387764404229 1052 -2812954313732796417 1057 -17857707129346166789 1058 -5269004761822808068 1063 -6111611187710453252 1067 -7026790498114792451 1071 -10421187431999041539 1074 -10962946788411535365 1077 -11508567829579753477 1082 -5586796232331222020 1087 -10664688802114188290 1091 -7101223008705196548 1093 -15730557498751873029 1097 -15622178182517453828 1102 -13023280475588667395 1106 -12736192986547669507 1109 -54449156097307139 1112 -7793412450098625026 1115 -13082902456897469447 1117 -11032923750376691202 1124 -768621266939883522 1126 -7680301716982797315 1128 -2529765954999401987 1131 -3092278718215461382 1134 -9721979104592801286 1140 -7638965909717933573 1146 -14786119811292616708 1151 -6950372959952776705 1155 -12443107208611825154 1156 -9339657551534781955 1158 -2637569863210881541 1161 -17203452450577793542 1166 -17969146007912340484 1172 -2200068367901267460 1176 -12757405660591319556 1180 -2215466944120424966 1184 -10360201018982236676 1190 -8757372217936483331 1194 -4619739097826383361 1197 -5258481580443618818 1198 -12767191547735252995 1200 -709131867671395842 1203 -14381518510326427139 1205 -7474380832040809474 1208 -10420528068240579075 1210 -4871472258172502537 1213 -14752872109699970567 1222 -1512107771448952322 1229 -8931318797796630019 1231 -17442635440581817347 1234 -6352160538927010308 1237 -7854574878209706502 1241 -12790477974029380098 1247 -1694268250394806274 1249 -18190476908103646723 1251 -6347737109409161731 1254 -18109962854929396739 1257 -18427225309876158471 1260 -9821587521358472709 1267 -6090671432051474948 1272 -10840793900646830595 1276 -16995529728013218306 1279 -3884460060356541442 1281 -11328174051558773766 1283 -2190762182871099394 1289 -10795994333162192385 1291 -5749185296348500997 1292 -15519907096103866882 1297 -4660993720606920707 1299 -865021105594269187 1302 -15732547829511033859 1305 -10370189278075376644 1308 -5643403162652225545 1312 -7491082473953581573 1321 -10267635999979525638 1326 -16917208132136155651 1332 -14938122712096231426 1335 -10458787752950376965 1337 -4661643778774054919 1342 -7378032314220749314 1349 -18262506560361936388 1351 -1777721411781745668 1355 -16284189979364697095 1359 -465731536183074823 1366 -1279204163871993862 1373 -1281083695929372167 1379 -1248628108208937989 1386 -16877732867220667397 1391 -12694354789085753351 1396 -11607902699193694212 1403 -4736550549618409987 1407 -3707377337025079300 1410 -15252497877242817539 1414 -1387328235986371593 1417 -6603032558061404163 1426 -17360253640789048836 1429 -4366853681792936456 1433 -12654867339467232261 1441 -17526554569512741895 1446 -17362261302665304067 1453 -1571924693327722498 1456 -5346032278378646020 1458 -4905765968575512581 1462 -6735440265877186050 1467 -2684582464658702853 1469 -12305455794035352066 1474 -10214763548894856708 1476 -5898818347968949767 1480 -8853876862443757062 1487 -1778255355643845123 1493 -9880096907488009732 1496 -14128719217189307399 1500 -1038097769717637636 1507 -16182828358028252163 1511 -9758783803467969029 1514 -5734508289193397251 1519 -17317991154864180229 1522 -3929011798945830403 1527 -2397676608572967937 1530 -15050420126488734213 1531 -9030951389346630659 1536 -6828609694434054148 1539 -15161672926172597254 1543 -12561916901436574724 1549 -13175017957196582402 1553 -5163667233751697414 1555 -8298531402410056193 1561 -3302216018490556417 1562 -8188022378496687108 1563 -6899994089344537605 1567 -14371356649301229570 1572 -6234142974089459201 1574 -8108331937702179332 1575 -6988695179225596420 1579 -4783268154102625283 1583 -5158962768530123268 1586 -8451852824882960900 1590 -16684923005574289925 1594 -435360132353818115 1599 -11179708428252470790 1602 -12560590898560496643 1608 -9052444957053236231 1611 -6942914933011863557 1618 -4685132753411777032 1623 -16127598791952926725 1631 -17139405263038539779 1636 -14416974839438770179 1639 -184674079454644738 1642 -15371113443157556742 1644 -6125564898470072836 1650 -6049476413298229761 1654 -2016046076376584709 1655 -9102901609995427333 1660 -14279380086715328002 1665 -16071171343743534084 1667 -3003661872837116932 1671 -1544053734049606146 1675 -17912839388887512068 1677 -18367485572659629066 1681 -62486557281101315 1691 -15341749693046047746 1694 -4462259451528626691 1696 -2198440243211776514 1699 -1739990500338348548 1701 -6736705032720670728 1705 -460022204996210692 1713 -13316259771467543558 1717 -11844773108515011076 1723 -13208087868677074947 1727 -15327490137854692355 1730 -12082487592532424709 1733 -16377323572476873736 1738 -0 1746 -622886781057602561 1746 -16338526270417904138 1747 -9002667202186955781 1757 -8853816989295250439 1762 -11704947860454198788 1769 -10586310579976363524 1773 -12274078960777730562 1777 -9504312777814311939 1779 -12938939440083727877 1782 -4482870732030096900 1787 -11469312931933075457 1791 -3010319078507518468 1792 -11746036803692802052 1796 -2845804857533704708 1800 -6113501269614124035 1804 -1300574605566718978 1807 -10066236704043377154 1809 -7972385596552911875 1811 -9467192894482478598 1814 -14898777861151503875 1820 -1944274375974874115 1823 -10386723956120624646 1826 -18320025862115844613 1832 -1387818835494813188 1837 -18296018469774430211 1841 -14855935328857078786 1844 -6009618969907709956 1846 -2589125298910864388 1850 -16233494802218098697 1854 -2481120375688643077 1863 -17348525962385858053 1868 -12345390066536357892 1873 -11898897602124667396 1877 -2640139887503545347 1881 -6231765986224989698 1884 -9439401787143523334 1886 -12293464382470856197 1892 -16085904910360067589 1897 -7252534814412510724 1902 -11702778818573550086 1906 -9474460568585535494 1912 -5394564267141025283 1918 -2149355951313254402 1921 -17924873918814083585 1923 -598191802606476803 1924 -8284578931129436164 1927 -10209356185787986442 1931 -1435256119122221570 1941 -1751234962195953666 1943 -4311520098760549381 1945 -14246310464944558596 1950 -12371324908926062594 1954 -1728194076248328196 1956 -3206212432676588545 1960 -608028375064114693 1961 -12735035537535459848 1966 -1161317385392644610 1974 -5963411090947829250 1976 -14747492552155802116 1978 -16061594303567645704 1982 -11401558272625990148 1990 -9760118054252460548 1994 -1590553955289263625 1998 -10340997646340822531 2007 -523412383725034499 2010 -611878128332703238 2013 -16958179163660410371 2019 -4049722845406929413 2022 -7868172937392666114 2027 -5918378978107988996 2029 -12787502422435844615 2033 -1060009293879599108 2040 -2360292516675315714 2044 -9882807690136518146 2046 -14696092264443978756 2048 -1467831371341617669 2052 -532975774202937346 2057 -11905286291268558854 2059 -531011598217908739 2065 -828942665123565063 2068 -16920169218690190853 2075 -7591945855482943490 2080 -14985689627450757635 2082 -14044560149551330824 2085 -17188203557127851010 2093 -12477774362747447814 2095 -1435155859536064516 2101 -1383226851914584068 2105 -5950622975418741254 2109 -11573330487936600066 2115 -3499677126004927489 2117 -7754519505545821700 2118 -3063606795384108551 2122 -9141715975960833030 2129 -184455528113873410 2135 -14290946452897026052 2137 -3303218105480425475 2141 -4921212561864013316 2144 -11659206998782866434 2148 -3483784077328335362 2150 -15349300100599457794 2152 -2991158137975641607 2154 -11671836184239777283 2161 -13903348973527331847 2164 -13972685059135606789 2171 -5067855901273612291 2176 -11203581229879824387 2179 -7398166954536630787 2182 -8137301061780033541 2185 -14461051202225956357 2190 -18364391265692359173 2195 -13202854215310531076 2200 -8735953751694682627 2204 -15668075878531113478 2207 -12181518985980303366 2213 -2996117190304599556 2219 -6321134583181571077 2223 -18292784224560228355 2228 -9730339360761143298 2231 -15423393047428954630 2233 -2289663845294497287 2239 -17293282718410542598 2246 -16848796704425902087 2252 -15868806819374243844 2259 -7170876063537340419 2263 -7450653208363214856 2266 -8532024281356555270 2274 -16059255557578908162 2280 -1877124332184628739 2282 -2891997770652300804 2285 -9380477352886239748 2289 -0 2293 -18421659490129039873 2293 -10046669908312212994 2294 -5751756535955732485 2296 -9063799367706849795 2301 -197150231261699074 2304 -12240146887946385922 2306 -14309589056601523716 2308 -1624985398293287428 2312 -7587234449739517958 2316 -15018247234507853314 2322 -14339897192752756740 2324 -241576182571638276 2328 -8692544418167008260 2332 -18142354925381021188 2336 -4357033544932333062 2340 -17783269028567291398 2346 -2150566123188780038 2352 -6826864893756137990 2358 -2570512448148033537 2364 -3926559756499773957 2365 -7320130823370788355 2370 -8279967564081321986 2373 -10487133814282217989 2375 -4474596476108096515 2380 -9162424448056197124 2383 -14148207203733810691 2387 -17544170151884681732 2390 -8376160833193445378 2394 -17178210789916656645 2396 -168694152552959492 2401 -8301334775237721091 2405 -5366880444274330116 2408 -2275685050251168772 2412 -5129073922893954566 2416 -7337914693295266824 2422 -9232645461774346756 2430 -9281558265232282115 2434 -17538929436013548545 2437 -8171970898838379521 2438 -407976145144453637 2439 -11917619836674704901 2444 -7463384942613006339 2449 -11628547823982306307 2452 -8367507709438611460 2455 -7426323158152134659 2459 -4692304143317559813 2462 -3164999859583895048 2467 -2475 -7352605297446749095 -7088047764357120496 -1873618248285620134 -15293063541372749212 -6979009792504496341 -3755115258874496180 -2534438137508857227 -7299919415988454234 -18411981910273949730 -17205553309096936780 -5248951067483899801 -7247233534530159373 -7192573503860179570 -6928015970770550971 -5302201063430293086 -14184058584951490062 -5354604945682204822 -7139887622401884709 -5727354401416546391 -7351759274033218447 -7087201740943589848 -6979855828815315184 -7299073392574923586 -3886521448658569638 -72483853 -6979009762410823886 -6927169947357020323 -3226253748217055407 -6926323880952529025 -15291089452348344358 -11366435697098950012 -13310078522289489702 -28484163972630126 -619366483213223113 -2795970204732164463 -6206321690771458217 -7087201710849917393 -15287705195328439433 -111740332 -7034515829391622532 -15882855708138472805 -7299073362481251131 -13237708561380147209 -9273081553817896179 -7246387481022956270 -18029523497279423740 -6927169917263347868 -13513632858282984745 -7193701599564661409 -7139041568894681606 -15802896446603200075 -7086355687436386745 -7033669805978091884 -17956211270007850924 -4563816462554630517 -11129540511026317143 -13237708531286474754 -7194547635875480252 -9003675253593868694 -18413109975884759114 -17205553334892496668 -7193701569470988954 -1413046541639878608 -946825488890267006 -7033669775884419429 -7352887339644027831 -12155898262057583711 -2989761681675846645 -6980983894426124568 -14604797433968658301 -18412263952471228466 -7245541427515753167 -15292781524969719388 -15846041951231805521 -7192855546057458306 -1873618493337240604 -6928298012967829707 -11016920455496205379 -15289961373839132475 -7085509633929183642 -10531295876509927661 -9870724660790479 -17144416678735644465 -429916096632 -7353733375954846674 -7032823752470888781 -638094591060870297 -16092841662109387098 -7301047494496551813 -8541237495103949042 -7818890396069135776 -5178673286195185763 -8250235334236768512 -1841584140945524876 -18412263922377556011 -6928297982874157252 -7141015700909982288 -7192855515963785851 -1836789879294462027 -9870724716497203 -14947088600271026 -13702113181256910523 -7140169634505490990 -10225919154718574698 -7352041286136824728 -7087483753047196129 -10864467494618334192 -1773948700787017845 -417019004815 -7032823722377216326 -14193031196214429556 -7299355404678529867 -17264773178461980386 -1278770520813602818 -6980137840918921465 -18411417898964025363 -11722522519282124676 -7192009492550255203 -9870725379720958 -6153353810106713963 -4536471466122020769 -6927451959460626604 -1127379439846033088 -6204347640438458139 -7139323611091960342 -7141015670816309833 -292816209 -9407029712610658618 -6600019782220383520 -7300201440989348710 -1682680374277112930 -4859408499100419349 -7247515559531053849 -7299355374584857412 -18411417868870352908 -5272120000921863921 -16551960241296116093 -6927451929366954149 -17186370626574550605 -15292499534362314294 -7139323580998287887 -7086637699539993026 -7298509351171326764 -2625321593698060269 -7033951818081698165 -6979291787411718362 -282724672 -15692070679858513338 -6926605905953423501 -17630638801202448322 -7247515529437381394 -15043322321877076105 -2792885536610780866 -1845823063 -11773758314556753827 -6176104209096443228 -7072090857947137391 -16846695548644688878 -15291935475760760928 -15459386004803160013 -8343160331046226180 -152372727 -7034797854392517008 -13108405724425291903 -7354015418152125410 -7298509321077654309 -929260878924941285 -7033951787988025710 -16107267423789974637 -7246669506023850746 -5138916826085984492 -7245823439619359448 -28766150282052641 -11719741672025753203 -9945828332568577291 -15282335361333069253 -7085791646032789923 -1899651063193470519 -9391897706793207408 -7354015388058452955 -11398792770463140027 -7033105764574495062 -2572415617593705468 -8983295826499536249 -14732267094484321768 -6925759852446220398 -1714226175 -4310109704341227252 -4227285979208156735 -161798587300055395 -18413392000885653590 -1732546113203799682 -7246669475930178291 -90526560417220980 -2522831856378709294 -7193983594471883430 -1378362890422519435 -10318013789890151505 -1910933862240552025 -14683276054533375891 -7141297713013588569 -4847114588453668997 -7353169364644922307 -1934287791725413661 -7033105734480822607 -6981265919427019044 -450064107389060609 -11929763 -6980419853022527746 -18411699911067631644 -7193137571058352782 -7198684663705634814 -7192291504653861484 -17174064760732583170 -1801586532 -7139605623195566623 -524384476410218364 -2209547647594924308 -7032259711067291959 -7526932862385391531 -7353169334551249852 -7931231880446346780 -7300483453092954991 -3584013211340638230 -6981265889333346589 -18412545947378450487 -6979573829608997098 -18015051914722281720 -10683744 -7247797571634660130 -7193137540964680327 -18411699880973959189 -7140451659506385466 -14383047196674041 -14448472746991225173 -7352323311137719204 -7087765778048090605 -7139605593101894168 -33560403334269270 -7806125477758371395 -1574206491118601300 -16092233475575710729 -4721835318523791115 -15292499560155382992 -6979573799515324643 -13614615898053870661 -6927733984461521080 -14782687056266790897 -6926887918057029782 -7247797541540987675 -2843459484954527603 -1786086408025671247 -9289441360305064013 -7138759569688363520 -5566476498435049615 -6672837949675865094 -7352323281044046749 -5459976717197837927 -2325612370 -16165083394671708123 -7087765747954418150 -5566476532828210437 -109033155035858516 -7299637399585751888 -5246976991356848017 -6927733954367848625 -15292499547258292377 -7246951518127457027 -7244428084583335163 -7194265636669162166 -7086698151192823114 -10049924856077420313 -7086919724540887502 -7138759539594691065 -7298791376172221240 -7086073658136396204 -15289397375427675165 -8052454368722028838 -11251542888998503501 -14003328469233502159 -8631928107028383434 -18413674012989259871 -2528053666090387191 -2624757569491043976 -2907319229923985027 -6926041864549826679 -15291935475760891195 -14871830661500634496 -6154481914409125818 -7194265606575489711 -12484855343804385078 -17299513090802386086 -9870724574938647 -7086919694447215047 -7034233812988920186 -7353451376748528588 -15289961386737730869 -7298791346078548785 -7246105464620253924 -17094850760611596031 -6981547931530625325 -15287141218410562876 -12642214096204597433 -7193419583161959063 -18443164073222080895 -491719075 -2844912017228892087 -12020808312486430123 -6103488045384926186 -70256349 -8556410824327300183 -7033387789575389538 -12237172509275325915 -5893456428412241171 -1576180593040230658 -7353451346654856133 -1201738978701542472 -7032541723170898240 -7300765465196561272 -18412827959482056768 -17018729383741883913 -7193419553068286608 -16870411475888572178 -33278412725618157 -10808445661528262808 -15228627241849521321 -10895941260599690580 -7140733671609991747 -1893141935297136496 -7762829005246892330 -814482688 -1873618476140922442 -7352605323241325485 -7088047790151696886 -7033387759481717083 -29894172902294792 -7245259411113050821 -18411981936068526120 -7299919441783030624 -6980701878023422222 -15293063567167325906 -12913795363565864010 -18279521768686553293 -825323228049442984 -2366626164980320297 -7192573529654755960 -6928015996565127361 -7511405001902393465 -12020126521146280357 -17297538988880889455 -3169683337105049483 -4320397392167437764 -2996806038868067480 -13880529192106525749 -12258258950024070758 -9457728271880882414 -1948083978070788460 -7088047760058024431 -6747869491765118557 -396563964949038476 -2283061350295603413 -18411981905974853665 -9870724676257796 -6979009788205400276 -1873618463243831315 -7299919411689358169 -7247233530231063308 -7192573499561083505 -6928015966471454906 -13172383304428291992 -7139887618102788644 -9894329350782060506 -5622264594716035131 -7351759269734122382 -13970089714745935549 -7087201736644493783 -11780790212937254859 -5640826309532256347 -6250067602413848771 -7034515855186198922 -1997667752184056011 -6979855824516219119 -7299073388275827521 -8773391357768107870 -6927169943057924258 -5386265453423232211 -6979009758111727821 -2245985413502404712 -7139041594689257996 -6926323876653432960 -8472178325545420517 -7299073358182155066 -7034515825092526467 -13237708557081051144 -1249061871705589231 -7246387476723860205 -18413110001679335504 -2791055589805656257 -6927169912964251803 -916886386616763786 -15801863628634588225 -8864802475807081547 -2308422057122989461 -9870725183834332 -12011043047629784321 -7193701595265565344 -2203332293412784251 -4203593881757353951 -8253013595490092090 -7139041564595585541 -7758389320980628720 -7086355683137290680 -13519837262133135719 -6064762127302393958 -7033669801678995819 -6985684972259117296 -10277194893975947111 -11865320555827824922 -13067141397934573091 -2940293779302123205 -13237708526987378689 -14428924761094490470 -18413109971585663049 -7194547631576384187 -8910270495612602670 -7193701565171892889 -11835120105125054734 -7353733401749423064 -6922632561930405347 -7352887335344931766 -7033669771585323364 -18412263948172132401 -7245541423216657102 -6980983890127028503 -17070057731888842956 -2071645033680471512 -6156737994042444103 -13228094635257760069 -4799330753387759873 -6928298008668733642 -13889891850651109005 -7192855541758362241 -7085509629630087577 -13143536835616770002 -8031770806726101038 -9140276259359819415 -7140169660300067380 -17094376519214238045 -1693503902275602273 -5778348214553282767 -1773948726581593258 -7032823748171792716 -12757875056923772971 -7353733371655750609 -16812272571148667179 -1009366474452567451 -7301047490197455748 -18412263918078459946 -6108201112818484768 -2707637655416932544 -9407593762612381184 -6928297978575061187 -7141015696610886223 -5214737420442796133 -15394822466617411907 -7140169630206394925 -5109952608322914327 -7032823718078120261 -7087483748748100064 -7352041281837728663 -18411417894664929298 -16275783350713190092 -1092700946662950361 -7299355400379433802 -32432389312218921 -6980137836619825400 -6927451955161530539 -7604559457759135281 -5728764444738258095 -7192009488251159138 -7139323606792864277 -16857332490755245458 -3343560 -464309651771 -11312578691943499042 -10024345359443035686 -5353476841380448450 -882239428633953381 -12060668540762391720 -2052891733976941648 -6979291813206294752 -7300201436690252645 -18411417864571256843 -7247515555231957784 -5887022448955360480 -2787671392973227504 -7299355370285761347 -6619090118045271448 -879419277503366844 -2008078727273646424 -15865913240105781170 -14383064393516769 -12743882002561632192 -13555232411643742491 -15289397319538705607 -812124046 -1873618514833704373 -7139323576699191822 -7086637695240896961 -6908100892141816223 -7298509346872230699 -2695321185936868691 -1945545813249623166 -7033951813782602100 -15289397353931868102 -6979291783112622297 -9632241780191791292 -6926605901654327436 -1631882651478525035 -7247515525138285329 -7245823465413935838 -4442788352416548793 -4443634474708830204 -3239382674005951948 -7034797850093420943 -8597138013777561025 -7354015413853029345 -7033951783688929645 -7246669501724754681 -1873618519133194353 -5462796868327441521 -16038494049631274362 -7245823435320263383 -17008002440344700481 -10420287313124918500 -17164650024200374474 -154011658 -7193983620266459820 -9611454020314924519 -11422235346247419176 -7085791641733693858 -2621625390044283947 -7033105760275398997 -2577002748724381918 -7354015383759356890 -6678552048861382680 -6925759848147124333 -2118540511074649695 -18413391996586557525 -6980419878817104136 -11485232201928082493 -14154340218780386747 -7193983590172787365 -5357989108122126648 -6154199898006423584 -7141297708714492504 -7353169360345826242 -1873618252584388545 -15287141218410825094 -72513270612231730 -6981265915127922979 -6980419848723431681 -7193137566759256717 -4985063574640920329 -18411699906768535579 -14463310864315779363 -7192291500354765419 -8998114985810134320 -10207847739528775756 -12638147294677239230 -7139605618896470558 -15287141188317152890 -7032259706768195894 -7353169330252153787 -13725680859840121166 -7300483448793858926 -5257405381051615158 -6349026413187499377 -18412545943079354422 -6979573825309901033 -7193137536665584262 -15292499474174116987 -2789363547278214886 -7247797567335564065 -4223816177647289670 -1873618458944604000 -9181763921242359034 -4000515045254367287 -12910411188229637865 -7140451655207289401 -8515376925833300352 -7087765773748994540 -7139605588802798103 -7352323306838623139 -7299637425380328278 -8484110780235385151 -11640650963542673608 -14101060887774700 -6927733980162425015 -13724983541464369033 -6979573795216228578 -1419747374885111438 -6926887913757933717 -1930668198955452810 -7138759565389267455 -7087765743655322085 -5675796533981283930 -92079366 -7086073683930972594 -7299637395286655823 -14383047197066244 -6826664259619390926 -7246951513828360962 -6927733950068752560 -9870724846126197 -7194265632370066101 -11653222680468915374 -7822735708193293328 -7086919720241791437 -14378284476977055283 -11527067173424792740 -7298791371873125175 -7086073653837300139 -212338786 -7034233838783496576 -498701699429 -5514354658380875071 -4770844606942742432 -12507909061331388812 -16431008191725831459 -7558005371879032024 -6926041860250730614 -98436550 -15287987237524997760 -18413674008690163806 -571813020932180290 -162989216 -5152482613269497204 -12689613402799605552 -11847668771932013896 -7194265602276393646 -550243203354855282 -17971473236626703405 -8937544163598010547 -15091099644146747549 -7298791341779452720 -7353451372449432523 -7034233808689824121 -6981547927231529260 -7300765490991137662 -7246105460321157859 -18412827985276633158 -16505476999257589058 -12490303189019657716 -7193419578862862998 -11945800880170403371 -692605700306896591 -16294470476861867997 -11823044541028697510 -7033387785276293473 -7354297408760251366 -985637089627933081 -7032541718871802175 -2635993685958396071 -9894523072925796113 -7300765460897465207 -18412827955182960703 -3485224758199257354 -7193419548769190543 -12430143129524439379 -2380943364365616692 -9305145756033811687 -7140733667310895682 -7352605318942229420 -13486365294955398350 -18383714950999639313 -7033387755182621018 -7088047785852600821 -7299919437483934559 -9870724671539207 -7245259406813954756 -6100103917337838794 -2517386234133546535 -6980701873724326157 -5406444717745899883 -18411981931769430055 -7192573525355659895 -16358593412853139153 -6928015992266031296 -12424382439595705495 -7139887643897365034 -10906583509962065513 -2691365739792435341 -12452979941127620409 -2625321597996828341 -5558136804797384674 -17944163576803951650 -7299919407390262104 -18411981901675757600 -6979009783906304211 -6928015962172358841 -6926323902448009350 -7247233525931967243 -10714918364066023692 -14383042897708902 -9562777431589128006 -14399056675416442168 -7139887613803692579 -7087201732345397718 -7351759265435026317 -1094393045078770756 -6979855820217123054 -10709931946762503359 -7034515850887102857 -5782296465685677189 -7299073383976731456 -27356072568228011 -9870724725998874 -10305085813919844606 -12189320918056831076 -2625321585099736377 -6927169938758828193 -813631719 -2538734585 -6926323872354336895 -3722579929450415844 -7139041590390161931 -8109001260751259400 -2103039510800303200 -6591652942651787148 -17675072908219975424 -2113895771772946077 -13237708552781955079 -13964897540691461690 -7034515820793430402 -7299073353883059001 -4081176587746805898 -18413109997380239439 -7246387472424764140 -495542969232394639 -285870162 -14714880334493255623 -13730298810256196609 -7139041560296489476 -9870725380375539 -7193701590966469279 -7086355678838194615 -9087257516001788955 -6500635606105786312 -5353476845680198762 -6293435923465308245 -1042835216333014619 -3191712388857988942 -7033669797379899754 -2791055637096302358 -8238066788924721312 -236521418 -7245541449011233492 -6156456003434055403 -14045391212846450114 -5142301044413892722 -7194547627277288122 -32432363518297616 -33560403333547721 -18413109967286566984 -5779476207078738131 -9870724609606766 -16854238106389710928 -7396235467053271442 -13369405230387234641 -7353733397450326999 -12370327100086618251 -8954142712877746247 -153456695047357500 -7352887331045835701 -6153071780807312570 -7033669767286227299 -11662233534561978119 -18412263943873036336 -28484129580255433 -13672484538110837999 -7245541418917561037 -6980983885827932438 -1873618484739049391 -1469680747315529380 -6928298004369637577 -7192855537459266176 -5778348193057605937 -7085509625330991512 -421317903336 -7140169656000971315 -7353733367356654544 -4312966040774706541 -236453759050 -104307364886481272 -7472754 -7032823743872696651 -6980137862414401790 -18164832428166481311 -3342493899775477828 -7301047485898359683 -17149817495305717253 -18412263913779363881 -17947829764244047033 -15691697526684648453 -7141015692311790158 -1873618454645376268 -15685038007044278084 -7140169625907298860 -13816341447826081654 -13938987348593804675 -6366943767402055912 -12279042692635953334 -7352041277538632598 -7087483744449003999 -17619527108083517127 -7299355396080337737 -16737240503404332379 -14932478444660590859 -15273884660262765959 -18411417890365833233 -6980137832320729335 -15287705229721143272 -6927451950862434474 -7192009483952063073 -10354526296759273115 -460010554417 -5572809636517250537 -7139323602493768212 -13925023889216243624 -7300201432391156580 -1518418403436004696 -3523407702141503062 -6979291808907198687 -17299513159587988488 -7247515550932861719 -5269329687486663025 -1491033538627700799 -11619890057073068122 -9733674716123433175 -17461812064821184287 -15288833316828087421 -7139323572400095757 -2322094249834514694 -7086637690941800896 -3693573093146429379 -1873618527731189820 -1782138217079048145 -7298509342573134634 -7033951809483506035 -28202057289434486 -5150516383306155674 -6049123076685497339 -2625321585099868576 -15703528257170507956 -13819044485072465 -6979291778813526232 -7245823461114839773 -6926605897355231371 -12241985901088998680 -18141240613009360196 -16530489490799724510 -7085791667528270248 -1873618510533364055 -7354015409553933280 -2553424131400403162 -7034797845794324878 -6204347606046082147 -15292138696292895124 -7349438249010596137 -434216371783 -7246669497425658616 -29894241687898103 -7245823431021167318 -7193983615967363755 -11123291787522213546 -9870724570482570 -13819048784561829 -32150304124504126 -16911052854443771624 -7085791637434597793 -3866984390250988963 -7354015379460260825 -16425638839461283996 -11957902820310123780 -7033105755976302932 -2373415502940997012 -6925759843848028268 -18413391992287461460 -4141551246163838791 -14505034101877245842 -15292781563661912115 -6980419874518008071 -7193983585873691300 -2850570111682611307 -5034923912583317800 -10169273990111757991 -14375674833933502504 -7141297704415396439 -1467988623105721525 -5510538250602219377 -16573157102592722994 -7353169356046730177 -6981265910828826914 -18412545968873930812 -7861398105913427821 -8429692928904464468 -7300483474588435316 -13774681046469379319 -11406639409630546199 -18411699902469439514 -7193137562460160652 -5246976944067250249 -6980419844424335616 -13940478354871813627 -13701595356116681867 -16426766960961390571 -7192291496055669354 -5340613827489301924 -7139605614597374493 -11949535933962127511 -11248024162254325150 -5213027855012595380 -13150190137466815618 -6351416861983902064 -1467988623104476637 -7032259702469099829 -15292217517958891213 -9870725207164369 -755605599842665995 -18412545938780258357 -6979573821010804968 -17933750666284107619 -7300483444494762861 -7193137532366488197 -1880166547305399311 -7247797563036468000 -7140451650908193336 -7087765769449898475 -7352323302539527074 -17682574712751982576 -7299637421081232213 -10223260478366353693 -11663374006463433041 -9077802184452213114 -613166273235716174 -6979573790917132513 -2137124098070087866 -6927733975863328950 -878291220490619127 -15361003727878554622 -6926887909458837652 -29330208881903963 -2152780467316000177 -5357989129618589597 -7646567114842638318 -7138759561090171390 -1466296546184988453 -1873618239687690255 -16874641756321417469 -7086073679631876529 -1584662439 -1811826159652440291 -7299637390987559758 -12802594135359554633 -10846196352290785698 -18413674034484740200 -6927733945769656495 -7246951509529264897 -6926041886045307004 -5458848587100981366 -7194265628070970036 -429917013116 -7086919715942695372 -15287987297712736594 -10756726237632137002 -7034233834484400511 -15290807375757707399 -7086073649538204074 -7298791367574029110 -28202074486146284 -9392472737220396979 -14847634415788360916 -15289397366829483851 -6518315999997593014 -15290807392954288538 -6926041855951634549 -18413674004391067741 -7194265597977297581 -527217511716357463 -16363166289661921249 -17620704769380124659 -447112350000 -16699231963845560721 -13522668423781746725 -7298791337480356655 -7353451368150336458 -1894834111096227152 -7034233804390728056 -7300765486692041597 -18412827980977537093 -6981547922932433195 -7246105456022061794 -14266293419834869195 -9302231382455945522 -10803332050175068476 -7193419574563766933 -2211239741711975644 -11079423192795514730 -1945545830446729386 -7140733693105472072 -8758866611300140123 -15936300577150731882 -7354297404461155301 -1873618467543975108 -1040015035110261938 -2710531354671711585 -14424533014481667245 -7033387780977197408 -7032541714572706110 -1459422918 -9870724626318937 -12478172948446053915 -15292781563662043248 -1732546104605738522 -7245259432608531146 -7300765456598369142 -9944700266957440235 -13252643833684952629 -18412827950883864638 -4896553002078767161 -30458188511250577 -18359396230774917168 -7140733663011799617 -11272180868443080721 -16874923798518695576 -5885976095793742930 -7088047781553504756 -7352605314643133355 -7033387750883524953 -168849237244052984 -7245259402514858691 -18411981927470333990 -5249797159683556626 -7299919433184838494 -8328636978065704368 -6980701869425230092 -451651625195341092 -6928015987966935231 -1873618501935892373 -7192573521056563830 -11737382248731378999 -7139887639598268969 -775817183132189744 -15801421542798591337 -3273906465467926857 -17504806456180148447 -4999048390027118623 -5949516568421532902 -6979855846011699444 -14540810596246029752 -15290525359354677342 -17939521450163242260 -7318928098821080086 -7299919403091166039 -6979009779607208146 -18411981897376661535 -7081697764708451986 -7247233521632871178 -969023931668825518 -6926323898148913285 -8108333368108254535 -7139887609504596514 -18169457873000663376 -7087201728046301653 -17740106858442524965 -7351759261135930252 -7299073379677635391 -13010413693461201911 -6979855815918026989 -5910883915778885296 -7034515846588006792 -8618954206934992460 -7246387498219340530 -18129074234833831896 -6927169934459732128 -1873618540628542454 -7139041586091065866 -10442852986254656567 -2395019873428113205 -6926323868055240830 -468604683425 -8087249190780536753 -1251886678142749075 -2719268023842506192 -15484720670843995730 -7741956108160599025 -12822961480931869999 -7034515816494334337 -7029767686712723344 -10163069590560376057 -13237708548482859014 -7194547653071864512 -3698377064121436540 -7246387468125668075 -18413109993081143374 -3404560976776923069 -7139041555997393411 -7193701586667373214 -450980818488526510 -7086355674539098550 -9249568750854341715 -6153071806601889139 -1873618446048036460 -7033669793080803689 -7245541444712137427 -6980983911622508828 -18413109962987470919 -7990307559300466109 -13222096480399393786 -1197790749063775841 -12902749409780696076 -7194547622978192057 -5561192151909205460 -7085509651125567902 -3357575107008071546 -1873618544926787278 -7353733393151230934 -7352887326746739636 -8268575981438895233 -6716515055005533871 -6980983881528836373 -18412263939573940271 -7245541414618464972 -7192855533160170111 -15289115337529558626 -13365777358560822813 -3028626790437816712 -6471725307461895704 -6928298000070541512 -17785259814435424400 -848960360419623992 -3511510786883322944 -1873618497636533880 -7085509621031895447 -7140169651701875250 -7353733363057558479 -8373123327081842861 -7032823739573600586 -7352041303333208988 -2947421221745985235 -15287987271918683167 -747201041967810181 -7301047481599263618 -6980137858115305725 -17447639780156180862 -504901560953342357 -7141015688012694093 -2524806052879271814 -7140169621608202795 -10940245256426751073 -7087483740149907934 -14383051495835695 -6176793320558560244 -7352041273239536533 -7300201458185732970 -9870725054269136 -18411417886066737168 -6980137828021633270 -15169666149850743870 -7299355391781241672 -931541139053413675 -11888218815508972893 -6927451946563338409 -10585391818487171337 -7192009479652967008 -10367111403802331152 -13105031987622054228 -5541264770713322802 -7139323598194672147 -5432665585007396118 -879419204419586170 -7086637716736377286 -1388146554381010284 -14288862971683801479 -16285378285009240167 -39519722 -6979291804608102622 -14947330030372718319 -7300201428092060515 -9009348648356481082 -9870725377033977 -14338844047743256282 -7247515546633765654 -10576074740845971129 -15288269284021110607 -16485145185525761680 -4927877544240416780 -7086637686642704831 -7034797871588901268 -7298509338274038569 -6979291774514430167 -7033951805184409970 -6926605893056135306 -7245823456815743708 -5701497137720395794 -13553803778814903538 -15291089469543613858 -15293909556187366880 -7085791663229174183 -7034797841495228813 -3459957951274485460 -7354015405254837215 -7872981627489289774 -6925759869642604658 -13088377121872283058 -5780604350074062210 -7246669493126562551 -17785259857426319623 -9870724661970361 -14101043691849748 -18413392018082037850 -4009970402599045444 -12596269962708321697 -7245823426722071253 -15417905864638990439 -13819027288885075 -2235142484806141593 -7193983611668267690 -15290807375757968455 -2061656954403227379 -13749155634582652047 -7085791633135501728 -5352762575340504654 -9231301807315879437 -7033105751677206867 -11733354 -6925759839548932203 -879419277504153303 -18413391987988365395 -6980419870218912006 -7093139039356454668 -245051885476 -7193983581574595235 -7192291521850245744 -5670079292018853456 -1731418116379051588 -7141297700116300374 -3295137431799203195 -15287141209812633373 -7353169351747634112 -7300483470289339251 -6981265906529730849 -18412545964574834747 -18411699898170343449 -6980419840125239551 -7193137558161064587 -7140451676702769726 -9282455518003529325 -7192291491756573289 -13517017123900556406 -12909283101122299560 -13001682132658816270 -7139605610298278428 -268323666682316511 -434215519994 -5195983173921998653 -7032259698170003764 -12400858092474729508 -1049541662233592999 -1652992377389188147 -9870724610131038 -6979573816711708903 -18412545934481162292 -7300483440195666796 -8122508159220254825 -6926887935253414042 -4854750274073986812 -7247797558737371935 -7140451646609097271 -8603359422188488186 -7087765765150802410 -7352323298240431009 -17228001359505262946 -830681586991892086 -16575900413863724702 -7299637416782136148 -5223960103625950402 -5622546645510588659 -6927733971564232885 -104307364887005000 -6926887905159741587 -15894193095142279476 -15412359869797107874 -7138759556791075325 -1466296571978319404 -6598609717403059389 -7086073675332780464 -3461791464603256049 -2624757569490846836 -18390735680858949823 -7299637386688463693 -6926041881746210939 -18413674030185644131 -7246951505230168832 -7194265623771873971 -2388013311603639908 -2661960728632166488 -1626046306172405994 -3292988330812113977 -7086919711643599307 -3350348129897809669 -7298791363274933045 -2949113337358451846 -13819040186107746 -7086073645239108009 -460011078998 -5726358075983398113 -3531693765072848000 -13521137881826199057 -7034233830185304446 -14262234001769891808 -15896859674140673387 -7246105481816638184 -15586729198849165186 -12999620585941961130 -6926041851652538484 -5515482706794513640 -18413674000091971676 -8245128912192341302 -4935175925304394923 -2914587895472588825 -16743444847067268462 -5511666307614640418 -9870724573497757 -10183587777134266649 -17461812064821708117 -7032541740367282500 -9870724662102366 -3000062970512082916 -7034233800091631991 -7353451363851240393 -4764015496858961551 -6981547918633337130 -18412827976678441028 -7246105451722965729 -7300765482392945532 -1413046558836524843 -9870724848157861 -6864771691154835689 -7193419570264670868 -2836065437668410391 -7140733688806376007 -5569296739845342906 -988739295852300565 -7033387776678101343 -11272445595878688522 -7354297400162059236 -14795036413642344215 -32432389311890484 -7706082920376043056 -5745384143842640959 -7245259428309435081 -7032541710273610045 -6493133948225848344 -6980701895219806482 -18412827946584768573 -2672695638434121742 -16252369836876630326 -1731136082779636780 -7300765452299273077 -4980727526481134825 -1623790192145992094 -5462796864028870900 -2573543666009113907 -14648423506775771260 -5344573059049392338 -13159245476491953264 -3016351 -28202074485294367 -8850604587316807925 -9433463473978082775 -8898527545369692553 -13497936734142399877 -7140733658712703552 -9242938543921759733 -17504292068232266828 -9870724716561847 -9462223648962578817 -7352605310344037290 -7088047777254408691 -6980701865126134027 -7245259398215762626 -18411981923171237925 -15082773189987797150 -7299919428885742429 -7247233547427447568 -11231012565665252996 -1873618497636796488 -6928015983667839166 -7192573516757467765 -8392045041254729237 -214958343501 -232154924118 -8773072981860484929 -17623817999021377174 -7139887635299172904 -13669583310057374915 -11952920104999651164 -7351759286930506642 -6095117356077024274 -6979855841712603379 -11911353550168000303 -4094565860123281828 -6979009775308112081 -2574671718721652501 -15289115337528575529 -6461237536894486399 -6588601354970859314 -829835576475584056 -12171433882329483024 -10601803635978405977 -6926323893849817220 -15798241638577277014 -13487493291780737431 -7247233517333775113 -7139887605205500449 -8339857016756570529 -32432363517708539 -7351759256836834187 -5781052833705625408 -7087201723747205588 -1873618501936285893 -161798583001876730 -7034515842288910727 -1413046550237676700 -10371546090728785591 -7579950199755704475 -15838740426298100091 -7299073375378539326 -6979855811618930924 -7246387493920244465 -1466296524689441838 -6927169930160636063 -6151097687482829151 -7515235746198914183 -2625039568697361387 -12568193962150069461 -7139041581791969801 -16683469352030111006 -1644749190600984028 -6926323863756144765 -3207856338718361997 -7086355700333674940 -11303992210264950105 -9391897685297203230 -12384683746431141169 -8177758353543530791 -17532367729421977465 -10164317815380509716 -13237708544183762949 -8111257460758677865 -7246387463826572010 -14101039392621716 -18413109988782047309 -7194547648772768447 -12269921124804134294 -5301355009923286798 -7193701582368277149 -460011210063 -7086355670240002485 -17641922897637607789 -14095300710942181622 -2436256590268073848 -7352887352541316026 -7033669788781707624 -7245541440413041362 -2431982719322294696 -6980983907323412763 -7563081637033018877 -4409898123206658145 -5046514566388713097 -14814059329512276972 -15800256793861162595 -11057351737720178980 -17330360441647925497 -7194547618679095992 -1873618523431110529 -11242798885672060886 -3189961255353123861 -7085509646826471837 -12105446909435184997 -17097227825074210038 -1626046323367871520 -7353733388852134869 -13819010092565514 -12633597574996166830 -16921919712692601988 -7352887322447643571 -7301047507393840008 -28202057290090175 -6980983877229740308 -13520119278536099331 -7645347347796264438 -9622122612430014405 -18412263935274844206 -7245541410319368907 -7192855528861074046 -9870724653384918 -5885976121588451299 -6928297995771445447 -7085509616732799382 -7140169647402779185 -10769578373240915520 -9819715104592692881 -7087483765944484324 -7352041299034112923 -7032823735274504521 -33560399034844152 -7301047477300167553 -6980137853816209660 -10944945458828937260 -13669905534306158122 -18026193457867982798 -5993604028623815795 -28766124487935334 -7192009505447543398 -5564352154681083943 -7141015683713598028 -1839019217048569077 -15288269228133516664 -7300201453886636905 -7087483735850811869 -6718814190576731851 -13940760358377162540 -7352041268940440468 -7299355387482145607 -6980137823722537205 -18411417881767641103 -6927451942264242344 -7192009475353870943 -15290807461739890121 -2188969976 -8455366527925159925 -18068492228830365096 -14880915248833168401 -14101065186935955 -7139323593895576082 -14311414805250836811 -9870726375998595 -7086637712437281221 -4958753984641959947 -13105552693218576372 -6979291800309006557 -7300201423792964450 -16165083394671772777 -13808562234416563902 -6926605918850711696 -7247515542334669589 -2624757565191619772 -5881584636093073417 -32150338517076320 -785983290288965090 -7086637682343608766 -7034797867289805203 -2979056014524680069 -9870726617761288 -14540120362423748755 -7298509333974942504 -1521238580360316800 -12976673671053247650 -7033951800885313905 -6926605888757039241 -7245823452516647643 -17952330525185607811 -9870725377427411 -15291935458564376817 -1603012832 -7085791658930078118 -6488020650427614617 -12919095062694397133 -7034797837196132748 -7354015400955741150 -6925759865343508593 -18413392013782941785 -7246669488827466486 -4938567614984488112 -9870724720559650 -7498048152822089063 -7193983607369171625 -7141297725910876764 -27638110467000252 -16349719711739348122 -11636885203347441199 -9106532716476499902 -4548898661544101372 -7085791628836405663 -15502606039934830517 -7562235613620865220 -7033105747378110802 -14426789124208003302 -8393023341868484594 -1251886678143272957 -15788821327926658275 -9870725383784222 -18413391983689269330 -6980419865919815941 -6925759835249836138 -5357989095224838157 -33842436932831545 -7192291517551149679 -11348511994799130885 -1349355141337056383 -7141297695817204309 -1201533247087773761 -32714336928793738 -4543926375081576497 -191950179678159950 -7032259723964580154 -11040187364284696123 -7353169347448538047 -14686192518692538500 -13757953135961245442 -18412545960275738682 -2185568528717121049 -7300483465990243186 -14835653469557491533 -1572796400505981136 -9788517128793228302 -6981265902230634784 -15290807375758362619 -7193137553861968522 -16118290484606469999 -16418446140666480289 -18411699893871247384 -16862372087833823519 -4206448621224592673 -7192291487457477224 -7140451672403673661 -455853151859508766 -5962976017044669465 -7139605605999182363 -8666576866089109507 -7032259693870907699 -6979573812412612838 -7300483435896570731 -8344402900088457576 -18412545930182066227 -5727354427211187592 -2625039560098645916 -5515200746281044358 -6926887930954317977 -5247593352906999919 -7247797554438275870 -7140451642310001206 -7087765760851706345 -7073611506224269312 -7355013769965733687 -13174099600966486052 -7352323293941334944 -11443887989255375246 -4146446263661364495 -7299637412483040083 -1571386258306042085 -5946179046742821181 -7246951531024745222 -6927733967265136820 -10639573055207376941 -6926887900860645522 -6994602712723555674 -5299098861505284222 -15289961412532110933 -5353476845679871617 -13513632884077627122 -7138759552491979260 -1144540847698347111 -7086073671033684399 -1901711630905639974 -31586254121535116 -9870726869747307 -5996988225456506285 -4526720883860178668 -2052034288295085986 -18413674025886548066 -2624475548788787270 -6926041877447114874 -8839539261532342355 -7246951500931072767 -4197549521920329467 -5197111291121763775 -2118540506775095159 -825887278052213957 -7194265619472777906 -5476138965309065280 -16588826745223579283 -7086919707344503242 -7034233825886208381 -7298791358975836980 -7603297094028494723 -7246105477517542119 -6981547944427913520 -6926041847353442419 -5353326642538940524 -16771372229407671518 -18413673995792875611 -1873618501935302415 -9091855447660366104 -2624475535891695220 -5248721418517808451 -695707940923638942 -1784668047839200521 -5123966563937617496 -9476180711086229037 -15289397293744457077 -2363364065503545615 -4187198287186888894 -14772210831162279241 -155104173409961345 -7032541736068186435 -7353451359552144328 -1744698334901438782 -1720270693428495876 -5863211220867680084 -7300765478093849467 -6981547914334241065 -18412827972379344963 -7246105447423869664 -1375340101 -1166767807772951618 -7193419565965574803 -15287141235607340513 -7140733684507279942 -9870726915359992 -427623910551390043 -7354297395862963171 -7352605336138613680 -7033387772379005278 -10744889200825600252 -12314414645940914243 -5835546371352167427 -7032541705974513980 -10066835850599860118 -14525018628252960934 -6980701890920710417 -7245259424010339016 -14735929132254102647 -13413734718255400134 -27920045185435350 -4259434058519872846 -11061538999702718852 -13453688972047942746 -4689994313342912648 -9870724605543603 -15693612750414022248 -7140733654413607487 -21103793 -1576744569957320370 -14491088305759127384 -14386655907411854458 -7088047772955312626 -28484120982325551 -7352605306044941225 -7245259393916666561 -6587273635278948726 -14242239340761843231 -18411981918872141860 -5885543833259738430 -6980701860827037962 -7299919424586646364 -7192573512458371700 -8271097636514235902 -7247233543128351503 -6928015979368743101 -7139887631000076839 -688165077222754128 -2596888135805175368 -805831884 -7351759282631410577 -7087201749541781978 -6979855837413507314 -2730758514787288045 -8477529075407324712 -6971520520943961491 -6979009771009016016 -1996539656478525349 -2783379539012682990 -9840570834876761539 -7247233513034679048 -5831598141713614638 -6926323889550721155 -8795062046652761419 -12021254591056840488 -8901578402287847519 -7351759252537738122 -8386285046382069117 -7087201719448109523 -7299073371079443261 -6979855807319834859 -7034515837989814662 -7218388648619280099 -10542598463376393433 -6927169925861539998 -7246387489621148400 -12120495559683868478 -13802551532558419223 -7139041577492873736 -11739843167758321863 -7086355696034578875 -17274157458060281885 -13267938307966371294 -12545157304493803906 -1413892608044762406 -13237708539884666884 -8487173076311541556 -18413109984482951244 -5991347858709874014 -30740204913755543 -7246387459527475945 -7194547644473672382 -17199514878479894463 -468607961144 -7193701578069181084 -11847668836417537503 -7086355665940906420 -1585891583597414023 -2131214311901497365 -7352887348242219961 -7033669784482611559 -9870725370086799 -12482638774054029707 -851940374707963849 -85348920764663585 -7245541436113945297 -27638024484357488 -6980983903024316698 -1785796031766792562 -32432333424428255 -7192855554655650436 -3909861025052296211 -8482084103035029713 -7194547614379999927 -6313103561496593691 -7085509642527375772 -5674063562281978264 -15287423178926065096 -7353733384553038804 -15398206672049014438 -14477731067643037065 -7301047503094743943 -7352887318148547506 -1584875219904694498 -6980983872930644243 -18412263930975748141 -1065200468752795466 -15177810505157773571 -6928297991472349382 -8054291783896991432 -12361766690509292773 -1053111882941597244 -7192855524561977981 -16593903001780618524 -5782296379703953450 -7141015709508174418 -7085509612433703317 -10965002685862053593 -985637059534194074 -7140169643103683120 -9870726291523332 -16878319014468388994 -1731418094883636884 -7352041294735016858 -7032823730975408456 -7087483761645388259 -6980137849517113595 -3232458045602335021 -827861392871065158 -7301047473001071488 -6446727831346940293 -5779476262966527373 -7192009501148447333 -6927451968058818734 -2625321597996762500 -245619828089948399 -7141015679414501963 -6722191038494869768 -7882137511871513597 -7087483731551715804 -7352041264641344403 -9870725382801118 -9272877412921575399 -7300201449587540840 -9870724775282169 -16436379939763521713 -17202169176751932566 -7299355383183049542 -18411417877468545038 -10754752208794749200 -7192009471054774878 -2430620139577280307 -8611086590374315409 -6927451937965146279 -805963576 -15291935514452624488 -7139323589596480017 -10812503129128502459 -8959988790532114083 -7086637708138185156 -2783943580416542774 -14720813411196273775 -10364150078460660033 -14362448656949381213 -15043322265989679329 -7300201419493868385 -7298509359769518894 -6979291796009910492 -1575898520748295875 -14922019929361876657 -5672976447337596765 -5831598141713745162 -6926605914551615631 -7247515538035573524 -2624475544489559534 -16260410401650050515 -2246314295 -7086637678044512701 -1873618544926328642 -7034797862990709138 -1096931248591407021 -7298509329675846439 -7033951796586217840 -7246669514622042876 -17941158134117828894 -10269743533867795671 -5347675226580976647 -3417860786310088910 -15688521365104690176 -7245823448217551578 -5134111493730665580 -5674104474255755027 -7717191090462393385 -6926605884457943176 -3596136887273981730 -32996417817806584 -7085791654630982053 -11573430836645528786 -7033105773172687192 -7002851749355521472 -5003335381653063809 -7354015396656645085 -7034797832897036683 -18413392009483845720 -7246669484528370421 -13979993538309916391 -6925759861044412528 -7193983603070075560 -10992311089537484262 -16267581173591573925 -2622783424577865887 -13819035887273179 -7141297721611780699 -16904712944498902101 -7033105743079014737 -6981265928025211174 -15289397375428134676 -4858945738627876093 -6149598375218383178 -6925759830950740073 -9870725377820485 -18413391979390173265 -6888657822347363642 -18411699919665823774 -1256116924183939300 -95187572621051114 -6980419861620719876 -7192291513252053614 -18103065826041137485 -10222816969990211254 -7141297691518108244 -7353169343149441982 -1283514803232966177 -2002437706943169624 -7032259719665484089 -5458807696621832226 -7300483461691147121 -7912204187237025199 -5570988786673059738 -18412545955976642617 -10756162204825814253 -6981265897931538719 -8790289859117385450 -13727996608218204062 -7193137549562872457 -18411699889572151319 -7247797580232852260 -12994192120810309276 -7140451668104577596 -7192291483158381159 -12595141854107535638 -530653668674242947 -7352323319735911334 -828143439366588588 -17782439637510194255 -9302231356660189158 -9870724574152005 -6702109864571701358 -7139605601700086298 -12221091663415674606 -7032259689571811634 -6818388851727926589 -6241248994235910470 -1495860772 -16414620023533864954 -6979573808113516773 -6926887926655221912 -764904305988667453 -1945545821847553660 -7247797550139179805 -257824245644068756 -32714354125768093 -5582375653047993645 -7138759578286555650 -7140451638010905141 -7087765756552610280 -18216242462685529942 -9870724653909453 -7352323289642238879 -14665067898537833 -8621549700391504088 -7299637408183944018 -9870726605571596 -7246951526725649157 -6927733962966040755 -1535116265 -11108455537719445229 -6926887896561549457 -3189738274038088907 -546637851 -2835565962349970524 -11203378729712550962 -5095261602624047103 -7138759548192883195 -7086919733139079632 -7022272817292838188 -8784323283773883651 -1559288237147293403 -7086073666734588334 -8223467025204184800 -13165236096819726188 -15603385639994459498 -18413674021587452001 -7246951496631976702 -6926041873148018809 -6206603763061885007 -67325673392440852 -1760429919 -7194265615173681841 -7719447165796222449 -848307201366754599 -7086919703045407177 -1300970856318961162 -7034233821587112316 -481505314442 -7353451385346720718 -16860434610997626540 -7298791354676740915 -9870724715972410 -7246105473218446054 -6981547940128817455 -2710500877822855357 -8910392170934372316 -9870724821026326 -11204987448696309768 -9538952396691932645 -16145254715685996093 -9026822436951032807 -933515197985065029 -17256118064413607864 -9870724723574767 -15637617634654488066 -15167687298960066085 -3229097897723823367 -232710822722865204 -7032541731769090370 -1095239150174733620 -7353451355253048263 -5661738430124328056 -7246105443124773599 -948019863933683111 -12569464285503162148 -18412827968080248898 -12806893037197853951 -6981547910035145000 -7300765473794753402 -7193419561666478738 -8781693865030060215 -9350227582874420345 -2146416191707613108 -5141454990908000668 -7140733680208183877 -99231065340118997 -1384869222252742270 -7088047798749889016 -2755882956846859405 -7033387768079909213 -7352605331839517615 -1854737581649037059 -7245259419711242951 -6980701886621614352 -7032541701675417915 -7192573538252948090 -15287141166821409947 -15009813827273492365 -1873618519132277025 -15859000273729226306 -9194029999668660035 -18061482934504720304 -15620129942127970446 -63936358548900149 -7088047768656216561 -1299603461807998560 -7352605301745845160 -1912255148622875001 -1884114781242131983 -1873618471842023452 -6979009796803592406 -18411981914573045795 -7299919420287550299 -16311679952720365302 -6980701856527941897 -11966722661119886674 -7247233538829255438 -16633803289536038978 -6293788 -7192573508159275635 -6928015975069647036 -7139887626700980774 -15287987246124173226 -7087201745242685913 -7351759278332314512 -10417684559046444665 -6187441192028997511 -6979855833114411249 -1627738421784085859 -9821766011289404660 -2201358182892963955 -2917029403468826036 -6979009766709919951 -6927169951656116388 -7247233508735582983 -6926323885251625090 -15292217457771480209 -17852636614813549786 -7351759248238642057 -7087201715149013458 -1873618510534673791 -7034515833690718597 -17119958815989303480 -9440398924256576696 -7299073366780347196 -10246365349637458074 -13237708565679243274 -6927169921562443933 -3124549171711117404 -7246387485322052335 -6173800107753209956 -8070402889435842339 -7193701603863757474 -5903201844931986950 -9942444152932271115 -7139041573193777671 -17445466366832674018 -7086355691735482810 -16337104745239479432 -39608547845342982 -11685864050243995219 -9870724855301167 -2624475527293240350 -12073088508578760599 -7344550947391473690 -13237708535585570819 -103179325071230022 -10145345834749004124 -15248610667087201439 -7194547640174576317 -18413109980183855179 -9612643166216521167 -2292825785040635134 -464308864142 -7193701573770085019 -7406197765746854162 -16499980877748898004 -8868431006280779480 -8692390532950198617 -10906015674175259968 -7086355661641810355 -7352887343943123896 -10864636259949152234 -8099428861213869098 -4451132852614006819 -7033669780183515494 -6814198485741601955 -7245541431814849232 -12239027656080492550 -6980983898725220633 -9789319859965200232 -6928298017266925772 -7192855550356554371 -10277198458468566225 -10155047267998762621 -33560403333744444 -11548493110908748854 -7085509638228279707 -9870727015827078 -7852905094729042252 -5890488336741041777 -7032823756769984846 -7353733380253942739 -9870724569302118 -2390363305267037366 -7352887313849451441 -7301047498795647878 -2785415304742897065 -5197675268039181352 -2440417245444768861 -18412263926676652076 -18006978259953256063 -1148489064437843751 -6209141837602293106 -6928297987173253317 -7141015705209078353 -7192855520262881916 -6698767362340226194 -7140169638804587055 -1873618501935826611 -7032823726676312391 -7087483757346292194 -15287141265701537621 -7352041290435920793 -9391368372458883286 -7301047468701975423 -18411417903263121428 -15274450213741529511 -2576363855830648001 -6980137845218017530 -7299355408977625932 -16414819240624849959 -7192009496849351268 -12524340376691672164 -29048140890506751 -17786951981635864475 -11226550812567013593 -6927451963759722669 -5812662174917002591 -12350988444867429435 -16904712944497919197 -6444753720828101815 -8249591958531213230 -11026052076656986043 -7141015675115405898 -593494716095857647 -490104423410 -5558136817694804187 -5299098870104786949 -7300201445288444775 -507301005145 -7087483727252619739 -5903884228619468800 -18411417873169448973 -1873618489038734736 -5887800003173221669 -18441542378572155291 -7247515563830149914 -12588077379430713565 -7299355378883953477 -7563081654230386389 -6927451933666050214 -7192009466755678813 -13733412026998916877 -12944621759895439337 -3054485070182287068 -13297153724119713646 -7139323585297383952 -2625321580800574862 -7086637703839089091 -9603332665379260396 -6979291791710814427 -7033951822380794230 -7298509355470422829 -33278412726339059 -18029805427700663298 -7247515533736477459 -7410231591516636514 -6926605910252519566 -17946822069105788673 -7034797858691613073 -4261009806383449657 -447113660592 -7033951792287121775 -7298509325376750374 -7246669510322946811 -4534407021717685362 -882239441530390089 -7245823443918455513 -6926605880158847111 -15287141209813681996 -6155327963617691259 -7085791650331885988 -6520986101609792887 -7354015392357549020 -6208295814188697765 -17554608171616110273 -7033105768873591127 -7246669480229274356 -18413392005184749655 -2803649555476907539 -6925759856745316463 -2599235317102217269 -7193983598770979495 -434216567980 -8106071886713521650 -15746639385482889572 -7042686759039796981 -1154748910884030612 -7141297717312684634 -12243195703567124314 -1873618463243306305 -7353169368944018372 -12450159764203178109 -4148105506751382809 -7033105738779918672 -6981265923726115109 -33560368940713974 -17169981030136022953 -6980419857321623811 -4347925833116092296 -18411699915366727709 -7192291508952957549 -5623513681448666797 -33560403333875172 -9870724666886005 -7139605627494662688 -18158855401059780058 -31586288515352537 -15287987271918618035 -14808173258166110477 -7141297687219012179 -10906583505664280685 -7353169338850345917 -6210168527151630153 -7032259715366388024 -2126679883759814820 -2149273943172056336 -4347925901902415544 -30176172108350955 -7300483457392051056 -6209987959894705607 -18412545951677546552 -6981265893632442654 -7193137545263776392 -5303893075865503190 -1893500283777910651 -18411699885273055254 -7247797575933756195 -5081553090054654323 -7140451663805481531 -8247724729662703889 -15291935458565162174 -7139605597400990233 -7352323315436815269 -7087765782347186670 -7032259685272715569 -316743096613078208 -8854892867934423142 -6979573803814420708 -29612199490947173 -6926887922356125847 -8812300966546965802 -11722969585197581469 -7247797545840083740 -8270229298440046551 -6232418373048731093 -1873618252584978388 -7138759573987459585 -4977791693940394284 -7087765752253514215 -7352323285343142814 -7880607085181405609 -18332726249486944491 -3305725656990746261 -7299637403884847953 -5729046448243673423 -7246951522426553092 -71108415 -6927733958666944690 -17393050773869299132 -2928740603216529441 -7138759543893787130 -16150166559522883585 -10364366638615365781 -7086919728839983567 -13883199269509858330 -6478223379528812725 -6661826795216177518 -7086073662435492269 -1873618441748613473 -17733865212901721719 -12164227723825907371 -936899463602964420 -7246951492332880637 -10590197086357423139 -18413674017288355936 -6926041868848922744 -6736241924278258064 -16008494395543127400 -11957793163459888268 -7194265610874585776 -13819010092238498 -9966383549812704714 -27356055372105278 -14101043691783933 -11185827869741483157 -7086919698746311112 -7034233817288016251 -1411918557712287647 -18056758355458721953 -10913804840997816088 -7298791350377644850 -1627738408887257463 -7353451381047624653 -5565348488711768192 -2560318637201230152 -6981547935829721390 -7246105468919349989 -9181763891149276690 -8262357255499876300 -7193419587461055128 -496747170645214674 -10898149942002910916 -13913370016116443282 -5992475971611657642 -7353451350953952198 -9759732344438719525 -15945141101920192007 -7032541727469994305 -9735614383619770157 -18412827963781152833 -7300765469495657337 -10284111861439072375 -6981547905736048935 -7193419557367382673 -14137369733883168373 -2392267112159643424 -2150364374056305808 -5724666024857831081 -7140733675909087812 -7033387763780813148 -515898869092 -7352605327540421550 -7088047794450792951 -6980701882322518287 -7245259415412146886 -12083278685454862534 -5532932791479699590 -7192573533953852025 -4144026118870010131 -5248951067485144858 -6928016000864223426 -30740204914411808 -12245079979070720255 -5622546645511767792 -16663012318422238821 -15860561610643605152 -31304293607146765 -795150206 -785983290289883252 -100 -101 -102 diff --git a/python/cudf/cudf/tests/test_hash_vocab.py b/python/cudf/cudf/tests/test_hash_vocab.py index 698dcba650f..529552cb2d9 100644 --- a/python/cudf/cudf/tests/test_hash_vocab.py +++ b/python/cudf/cudf/tests/test_hash_vocab.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020, NVIDIA CORPORATION. +# Copyright (c) 2020-2021, NVIDIA CORPORATION. from cudf.utils.hash_vocab_utils import hash_vocab import os import filecmp @@ -7,18 +7,16 @@ @pytest.fixture(scope="module") def datadir(datadir): - return os.path.join(datadir, "vocab_hash") + return os.path.join( + datadir, "subword_tokenizer_data", "bert_base_cased_sampled" + ) def test_correct_bert_base_vocab_hash(datadir, tmpdir): - # The vocabulary is 5% drawn from bert-base-uncased - # sampling script at: - # https://gist.github.com/VibhuJawa/4fc5981d2cbba1ab8b1e78cdf6aede72 - vocab_path = os.path.join(datadir, "bert-base-uncased-vocab-5per.txt") + # The vocabulary is drawn from bert-base-cased + vocab_path = os.path.join(datadir, "vocab.txt") - groundtruth_path = os.path.join( - datadir, "ground_truth_vocab_hash_5per.txt" - ) + groundtruth_path = os.path.join(datadir, "vocab-hash.txt") output_path = tmpdir.join("cudf-vocab-hash.txt") hash_vocab(vocab_path, output_path) diff --git a/python/cudf/cudf/tests/test_subword_tokenizer.py b/python/cudf/cudf/tests/test_subword_tokenizer.py new file mode 100644 index 00000000000..bdb343a41f7 --- /dev/null +++ b/python/cudf/cudf/tests/test_subword_tokenizer.py @@ -0,0 +1,112 @@ +# Copyright (c) 2020-2021, NVIDIA CORPORATION. +from transformers import BertTokenizer +import pytest +import os +import numpy as np + +import cudf +from cudf.core.subword_tokenizer import SubwordTokenizer + + +@pytest.fixture(scope="module") +def datadir(datadir): + return os.path.join(datadir, "subword_tokenizer_data") + + +def assert_equal_tokenization_outputs(hf_output, cudf_output): + assert ( + np.sum(hf_output["input_ids"] != cudf_output["input_ids"].get()) == 0 + ) + assert ( + np.sum( + hf_output["attention_mask"] != cudf_output["attention_mask"].get() + ) + == 0 + ) + + +def test_subword_tokenize_on_disk_vocab_str_api(datadir): + """ + Tests the subword-tokenizer API where + the vocabulary is not pre-loaded + and is accessed via the string accessor + """ + with open(os.path.join(datadir, "test_sentences.txt")) as file: + input_sentence_ls = [line.strip() for line in file] + + vocab_dir = os.path.join(datadir, "bert_base_cased_sampled") + vocab_hash_path = os.path.join(vocab_dir, "vocab-hash.txt") + + ser = cudf.Series(input_sentence_ls) + tokens, masks, metadata = ser.str.subword_tokenize( + vocab_hash_path, + max_length=32, + stride=32, + do_lower=True, + max_rows_tensor=len(ser), + ) + + +@pytest.mark.parametrize("seq_len", [32, 64]) +@pytest.mark.parametrize("stride", [0, 15, 30]) +@pytest.mark.parametrize("add_special_tokens", [True, False]) +@pytest.mark.parametrize("do_lower_case", [True, False]) +def test_subword_tokenize( + seq_len, stride, add_special_tokens, do_lower_case, datadir +): + with open(os.path.join(datadir, "test_sentences.txt")) as file: + input_sentence_ls = [line.strip() for line in file] + + vocab_dir = os.path.join(datadir, "bert_base_cased_sampled") + + hf_tokenizer = BertTokenizer.from_pretrained( + vocab_dir, do_lower_case=do_lower_case + ) + + hf_output = hf_tokenizer( + input_sentence_ls, + max_length=seq_len, + stride=stride, + padding="max_length", + return_tensors="np", + truncation=True, + add_special_tokens=add_special_tokens, + ) + + vocab_hash = os.path.join(vocab_dir, "vocab-hash.txt") + str_series = cudf.Series(input_sentence_ls) + cudf_tokenizer = SubwordTokenizer(vocab_hash, do_lower_case=do_lower_case) + cudf_output = cudf_tokenizer( + str_series, + max_length=seq_len, + max_num_rows=len(str_series), + stride=stride, + padding="max_length", + return_tensors="cp", + truncation=True, + add_special_tokens=add_special_tokens, + ) + assert_equal_tokenization_outputs(hf_output, cudf_output) + + +def test_subword_tokenize_with_truncation(datadir): + vocab_dir = os.path.join(datadir, "bert_base_cased_sampled") + vocab_hash = os.path.join(vocab_dir, "vocab-hash.txt") + str_series = cudf.Series(["Test error"]) + cudf_tokenizer = SubwordTokenizer(vocab_hash) + + error_msg = ( + "Adding special tokens is not supported with truncation = False. " + "Custom Cupy kernel can potentially " + "be used to add it. For reference " + "see: _bert_add_special_tokens" + ) + + with pytest.raises(NotImplementedError, match=error_msg): + cudf_tokenizer( + str_series, + max_length=64, + max_num_rows=len(str_series), + truncation=False, + add_special_tokens=True, + ) diff --git a/python/cudf/requirements/cuda-11.0/dev_requirements.txt b/python/cudf/requirements/cuda-11.0/dev_requirements.txt index 4721694f59f..455258d2e2e 100644 --- a/python/cudf/requirements/cuda-11.0/dev_requirements.txt +++ b/python/cudf/requirements/cuda-11.0/dev_requirements.txt @@ -36,6 +36,6 @@ sphinx-copybutton sphinx-markdown-tables sphinx_rtd_theme sphinxcontrib-websupport -typing_extensions +transformers typing_extensions wheel diff --git a/python/cudf/requirements/cuda-11.1/dev_requirements.txt b/python/cudf/requirements/cuda-11.1/dev_requirements.txt index adc1619a619..4cad661b5c4 100644 --- a/python/cudf/requirements/cuda-11.1/dev_requirements.txt +++ b/python/cudf/requirements/cuda-11.1/dev_requirements.txt @@ -36,6 +36,6 @@ sphinx-copybutton sphinx-markdown-tables sphinx_rtd_theme sphinxcontrib-websupport -typing_extensions +transformers typing_extensions wheel diff --git a/python/cudf/requirements/cuda-11.2/dev_requirements.txt b/python/cudf/requirements/cuda-11.2/dev_requirements.txt index 7328e3f222a..a7e5f1c0993 100644 --- a/python/cudf/requirements/cuda-11.2/dev_requirements.txt +++ b/python/cudf/requirements/cuda-11.2/dev_requirements.txt @@ -36,6 +36,6 @@ sphinx-copybutton sphinx-markdown-tables sphinx_rtd_theme sphinxcontrib-websupport -typing_extensions +transformers typing_extensions wheel diff --git a/python/cudf/setup.py b/python/cudf/setup.py index 21d7ed56d58..b8c7dc5868f 100644 --- a/python/cudf/setup.py +++ b/python/cudf/setup.py @@ -40,6 +40,7 @@ "hypothesis" "mimesis", "pyorc", "msgpack", + "transformers", ] } From 1debb964315b84f7972c577ab0c32c07caf1d39f Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 3 May 2021 15:42:56 -0600 Subject: [PATCH 23/65] Implement concatenate_rows for list type (#8049) This PR closes #7767. It implements `lists::concatenate_rows` that performs concatenation of all list elements at the same rows from the given table of list elements. For example: ``` s1 = [{0, 1}, {2, 3, 4}, {5}, {}, {6, 7}] s2 = [{8}, {9}, {}, {10, 11, 12}, {13, 14, 15, 16}] r = lists::concatenate_rows( table_view{s1, s2} ) r is now [{0, 1, 8}, {2, 3, 4, 9}, {5}, {10, 11, 12}, {6, 7, 13, 14, 15, 16}] ``` Currently, only lists columns of one depth level are supported. Authors: - Nghia Truong (https://github.com/ttnghia) Approvers: - Robert Maynard (https://github.com/robertmaynard) - Devavret Makkar (https://github.com/devavret) - Ray Douglass (https://github.com/raydouglass) URL: https://github.com/rapidsai/cudf/pull/8049 --- conda/recipes/libcudf/meta.yaml | 1 + cpp/CMakeLists.txt | 1 + cpp/include/cudf/lists/concatenate_rows.hpp | 68 +++ cpp/include/doxygen_groups.h | 1 + cpp/src/lists/concatenate_rows.cu | 438 ++++++++++++++++++++ cpp/tests/CMakeLists.txt | 3 +- cpp/tests/lists/concatenate_rows_tests.cpp | 397 ++++++++++++++++++ 7 files changed, 908 insertions(+), 1 deletion(-) create mode 100644 cpp/include/cudf/lists/concatenate_rows.hpp create mode 100644 cpp/src/lists/concatenate_rows.cu create mode 100644 cpp/tests/lists/concatenate_rows_tests.cpp diff --git a/conda/recipes/libcudf/meta.yaml b/conda/recipes/libcudf/meta.yaml index ebdf6a3fb12..037bb70ab77 100644 --- a/conda/recipes/libcudf/meta.yaml +++ b/conda/recipes/libcudf/meta.yaml @@ -137,6 +137,7 @@ test: - test -f $PREFIX/include/cudf/lists/detail/drop_list_duplicates.hpp - test -f $PREFIX/include/cudf/lists/detail/interleave_columns.hpp - test -f $PREFIX/include/cudf/lists/detail/sorting.hpp + - test -f $PREFIX/include/cudf/lists/concatenate_rows.hpp - test -f $PREFIX/include/cudf/lists/count_elements.hpp - test -f $PREFIX/include/cudf/lists/explode.hpp - test -f $PREFIX/include/cudf/lists/drop_list_duplicates.hpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 1689de29b05..198690e37ff 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -263,6 +263,7 @@ add_library(cudf src/join/join.cu src/join/semi_join.cu src/lists/contains.cu + src/lists/concatenate_rows.cu src/lists/copying/concatenate.cu src/lists/copying/copying.cu src/lists/copying/gather.cu diff --git a/cpp/include/cudf/lists/concatenate_rows.hpp b/cpp/include/cudf/lists/concatenate_rows.hpp new file mode 100644 index 00000000000..1d93de418f8 --- /dev/null +++ b/cpp/include/cudf/lists/concatenate_rows.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +namespace cudf { +namespace lists { +/** + * @addtogroup lists_concatenate_rows + * @{ + * @file + */ + +/* + * @brief Flag to specify whether a null list element will be ignored from concatenation, or the + * entire concatenation result involving null list elements will be a null element. + */ +enum class concatenate_null_policy { IGNORE, NULLIFY_OUTPUT_ROW }; + +/** + * @brief Row-wise concatenating multiple lists columns into a single lists column. + * + * The output column is generated by concatenating the elements within each row of the input + * table. If any row of the input table contains null elements, the concatenation process will + * either ignore those null elements, or will simply set the entire resulting row to be a null + * element. + * + * @code{.pseudo} + * s1 = [{0, 1}, {2, 3, 4}, {5}, {}, {6, 7}] + * s2 = [{8}, {9}, {}, {10, 11, 12}, {13, 14, 15, 16}] + * r = lists::concatenate_rows(s1, s2) + * r is now [{0, 1, 8}, {2, 3, 4, 9}, {5}, {10, 11, 12}, {6, 7, 13, 14, 15, 16}] + * @endcode + * + * @throws cudf::logic_error if any column of the input table is not a lists columns. + * @throws cudf::logic_error if any lists column contains nested typed entry. + * @throws cudf::logic_error if all lists columns do not have the same entry type. + * + * @param input Table of lists to be concatenated. + * @param null_policy The parameter to specify whether a null list element will be ignored from + * concatenation, or any concatenation involving a null list element will result in a null list. + * @param mr Device memory resource used to allocate the returned column's device memory. + * @return A new column in which each row is a list resulted from concatenating all list elements in + * the corresponding row of the input table. + */ +std::unique_ptr concatenate_rows( + table_view const& input, + concatenate_null_policy null_policy = concatenate_null_policy::IGNORE, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + +/** @} */ // end of group +} // namespace lists +} // namespace cudf diff --git a/cpp/include/doxygen_groups.h b/cpp/include/doxygen_groups.h index f78ff98d49d..11b907e7f16 100644 --- a/cpp/include/doxygen_groups.h +++ b/cpp/include/doxygen_groups.h @@ -143,6 +143,7 @@ * @} * @defgroup lists_apis Lists * @{ + * @defgroup lists_concatenate_rows Combining * @defgroup lists_extract Extracting * @defgroup lists_contains Searching * @defgroup lists_gather Gathering diff --git a/cpp/src/lists/concatenate_rows.cu b/cpp/src/lists/concatenate_rows.cu new file mode 100644 index 00000000000..51df7255df9 --- /dev/null +++ b/cpp/src/lists/concatenate_rows.cu @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace cudf { +namespace lists { +namespace detail { +namespace { +std::unique_ptr concatenate_rows_ignore_null(table_view const& input, + bool has_null_mask, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) +{ + auto const num_output_lists = input.num_rows(); + auto const table_dv_ptr = table_device_view::create(input); + + // Interleave the list element from the input table, thus all the lists at the same row now stay + // next to each other. + auto interleaved_columns = detail::interleave_columns(input, has_null_mask, stream); + + // Modify the list offsets to combine lists of the same input row. + static_assert(sizeof(offset_type) == sizeof(int32_t)); + static_assert(sizeof(size_type) == sizeof(int32_t)); + auto list_offsets = make_numeric_column( + data_type{type_id::INT32}, num_output_lists + 1, mask_state::UNALLOCATED, stream, mr); + auto const d_offsets = list_offsets->mutable_view().template begin(); + + // The array of int8_t to store validities for list elements. + // Since we combine multiple lists, we need to recompute list validities. + auto validities = rmm::device_uvector(has_null_mask ? num_output_lists : 0, stream); + + // For an input table of `n` columns, if after interleaving we have the list offsets are + // [ i_0, i_1, ..., i_n, i_n+1, ..., i_2n, ... ] then to concatenate them just modify the offsets + // to be [ i_0, i_n, i_2n, i_3n, ... ]. + auto const d_interleaved_offsets = lists_column_view(interleaved_columns->view()).offsets_begin(); + thrust::transform( + rmm::exec_policy(stream), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_output_lists + 1), + d_offsets, + [d_interleaved_offsets, + num_cols = input.num_columns(), + table_dv = *table_dv_ptr, + d_validities = validities.begin(), + has_null_mask] __device__(auto const idx) { + if (has_null_mask) { + auto const any_valid = thrust::any_of( + thrust::seq, table_dv.begin(), table_dv.end(), [idx](auto const& list_col) { + return list_col.is_valid(idx); + }); + d_validities[idx] = static_cast(any_valid); + } + return d_interleaved_offsets[idx * num_cols]; + }); + + auto [null_mask, null_count] = [&] { + return has_null_mask + ? cudf::detail::valid_if( + validities.begin(), validities.end(), thrust::identity{}, stream, mr) + : std::make_pair(rmm::device_buffer{}, size_type{0}); + }(); + + // The child column containing list entries is taken from the `interleaved_columns` column. + auto interleaved_columns_content = interleaved_columns->release(); + + return make_lists_column( + num_output_lists, + std::move(list_offsets), + std::move(interleaved_columns_content.children[lists_column_view::child_column_index]), + null_count, + null_count > 0 ? std::move(null_mask) : rmm::device_buffer{}, + stream, + mr); +} + +/** + * @brief Generate list offsets and list validities for the output lists column from the table_view + * of the input lists columns. + * + * This function is called only when (has_null_mask == true and null_policy == NULLIFY_OUTPUT_ROW). + */ +std::pair, rmm::device_uvector> +generate_list_offsets_and_validities(table_view const& input, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) +{ + auto const num_output_lists = input.num_rows(); + auto const table_dv_ptr = table_device_view::create(input); + + // The output offsets column. + static_assert(sizeof(offset_type) == sizeof(int32_t)); + static_assert(sizeof(size_type) == sizeof(int32_t)); + auto list_offsets = make_numeric_column( + data_type{type_id::INT32}, num_output_lists + 1, mask_state::UNALLOCATED, stream, mr); + auto const d_offsets = list_offsets->mutable_view().template begin(); + + // The array of int8_t to store validities for list elements. + auto validities = rmm::device_uvector(num_output_lists, stream); + + // Compute list sizes and validities. + thrust::transform( + rmm::exec_policy(stream), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_output_lists), + d_offsets, + [table_dv = *table_dv_ptr, d_validities = validities.begin()] __device__(size_type const idx) { + auto const all_valid = + thrust::all_of(thrust::seq, table_dv.begin(), table_dv.end(), [idx](auto const& list_col) { + return list_col.is_valid(idx); + }); + d_validities[idx] = static_cast(all_valid); + if (not all_valid) return size_type{0}; + + // Compute size of the output list as sum of sizes of input lists + return thrust::transform_reduce( + thrust::seq, + table_dv.begin(), + table_dv.end(), + [idx] __device__(auto const& lists_col) { + auto const list_offsets = + lists_col.child(lists_column_view::offsets_column_index).template data() + + lists_col.offset(); + return list_offsets[idx + 1] - list_offsets[idx]; // list size + }, + size_type{0}, + thrust::plus{}); + }); + + // Compute offsets from sizes. + thrust::exclusive_scan( + rmm::exec_policy(stream), d_offsets, d_offsets + num_output_lists + 1, d_offsets); + + return {std::move(list_offsets), std::move(validities)}; +} + +/** + * @brief Compute string sizes, string validities, and concatenate string lists functor. + * + * This functor is called only when (has_null_mask == true and null_policy == NULLIFY_OUTPUT_ROW). + * It is executed twice. In the first pass, the sizes and validities of the output strings will be + * computed. In the second pass, this will concatenate the lists of strings of the given table of + * lists columns in a row-wise manner. + */ +struct compute_string_sizes_and_concatenate_lists_fn { + table_device_view const table_dv; + + // Store list offsets of the output lists column. + offset_type const* const dst_list_offsets; + + // Store offsets of the strings. + offset_type* d_offsets{nullptr}; + + // If d_chars == nullptr: only compute sizes and validities of the output strings. + // If d_chars != nullptr: only concatenate strings. + char* d_chars{nullptr}; + + // We need to set `1` or `0` for the validities of the strings in the child column. + int8_t* d_validities{nullptr}; + + __device__ void operator()(size_type const idx) + { + // The current row contain null, which has been identified during `dst_list_offsets` + // computation. + if (dst_list_offsets[idx + 1] == dst_list_offsets[idx]) { return; } + + // read_idx and write_idx are indices of string elements. + size_type write_idx = dst_list_offsets[idx]; + thrust::for_each( + thrust::seq, table_dv.begin(), table_dv.end(), [&] __device__(auto const& lists_col) { + auto const list_offsets = + lists_col.child(lists_column_view::offsets_column_index).template data() + + lists_col.offset(); + auto const& str_col = lists_col.child(lists_column_view::child_column_index); + auto const str_offsets = + str_col.child(strings_column_view::offsets_column_index).template data(); + + // The indices of the strings within the source list. + auto const start_str_idx = list_offsets[idx]; + auto const end_str_idx = list_offsets[idx + 1]; + + if (not d_chars) { // just compute sizes of strings within a list + for (auto read_idx = start_str_idx; read_idx < end_str_idx; ++read_idx, ++write_idx) { + d_validities[write_idx] = static_cast(str_col.is_valid(read_idx)); + d_offsets[write_idx] = str_offsets[read_idx + 1] - str_offsets[read_idx]; + } + } else { // just copy the entire memory region containing all strings in the list + // start_byte and end_byte are indices of character of the string elements. + auto const start_byte = str_offsets[start_str_idx]; + auto const end_byte = str_offsets[end_str_idx]; + if (start_byte < end_byte) { + auto const input_ptr = + str_col.child(strings_column_view::chars_column_index).template data() + + start_byte; + auto const output_ptr = d_chars + d_offsets[write_idx]; + thrust::copy(thrust::seq, input_ptr, input_ptr + end_byte - start_byte, output_ptr); + write_idx += end_str_idx - start_str_idx; + } + } + }); + } +}; + +/** + * @brief Struct used in type_dispatcher to interleave list entries of the input lists columns and + * output the results into a destination column. + * + * This functor is called only when (has_null_mask == true and null_policy == NULLIFY_OUTPUT_ROW). + */ +struct concatenate_lists_fn { + template + std::enable_if_t, std::unique_ptr> operator()( + table_view const& input, + column_view const& output_list_offsets, + size_type num_output_lists, + size_type num_output_entries, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) const noexcept + { + auto const table_dv_ptr = table_device_view::create(input); + auto const comp_fn = compute_string_sizes_and_concatenate_lists_fn{ + *table_dv_ptr, output_list_offsets.template begin()}; + + // Generate a null mask because the input table has nullable column. + auto [offsets_column, chars_column, null_mask, null_count] = + cudf::strings::detail::make_strings_children_with_null_mask( + comp_fn, num_output_lists, num_output_entries, stream, mr); + + return make_strings_column(num_output_entries, + std::move(offsets_column), + std::move(chars_column), + null_count, + std::move(null_mask), + stream, + mr); + } + + template + std::enable_if_t(), std::unique_ptr> operator()( + table_view const& input, + column_view const& output_list_offsets, + size_type num_output_lists, + size_type num_output_entries, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) const noexcept + { + auto const table_dv_ptr = table_device_view::create(input); + + // The output child column. + auto const child_col = lists_column_view(*input.begin()).child(); + auto output = + allocate_like(child_col, num_output_entries, mask_allocation_policy::NEVER, stream, mr); + auto output_dv_ptr = mutable_column_device_view::create(*output); + + // The array of int8_t to store entry validities. + auto validities = rmm::device_uvector(num_output_entries, stream); + + thrust::for_each_n( + rmm::exec_policy(stream), + thrust::make_counting_iterator(0), + num_output_lists, + [num_cols = input.num_columns(), + table_dv = *table_dv_ptr, + d_validities = validities.begin(), + dst_list_offsets = output_list_offsets.template begin(), + d_output = output_dv_ptr->template begin()] __device__(size_type const idx) { + // The output row has been identified as a null list during list size computation. + if (dst_list_offsets[idx + 1] == dst_list_offsets[idx]) { return; } + + auto write_start = dst_list_offsets[idx]; + thrust::for_each( + thrust::seq, table_dv.begin(), table_dv.end(), [&] __device__(auto const& lists_col) { + auto const list_offsets = lists_col.child(lists_column_view::offsets_column_index) + .template data() + + lists_col.offset(); + auto const& data_col = lists_col.child(lists_column_view::child_column_index); + + // The indices of the entries within the source list. + auto const start_idx = list_offsets[idx]; + auto const end_idx = list_offsets[idx + 1]; + + // Fill the validities array. + for (auto read_idx = start_idx, write_idx = write_start; read_idx < end_idx; + ++read_idx, ++write_idx) { + d_validities[write_idx] = static_cast(data_col.is_valid(read_idx)); + } + // Do a copy for the entire list entries. + auto const input_ptr = + reinterpret_cast(data_col.template data() + start_idx); + auto const output_ptr = reinterpret_cast(&d_output[write_start]); + thrust::copy( + thrust::seq, input_ptr, input_ptr + sizeof(T) * (end_idx - start_idx), output_ptr); + write_start += end_idx - start_idx; + }); + }); + + auto [null_mask, null_count] = cudf::detail::valid_if( + validities.begin(), validities.end(), thrust::identity{}, stream, mr); + if (null_count > 0) { output->set_null_mask(null_mask, null_count); } + + return output; + } + + template + std::enable_if_t and not cudf::is_fixed_width(), + std::unique_ptr> + operator()(table_view const&, + column_view const&, + size_type, + size_type, + rmm::cuda_stream_view, + rmm::mr::device_memory_resource*) const + { + // Currently, only support string_view and fixed-width types + CUDF_FAIL("Called `concatenate_lists_fn()` on non-supported types."); + } +}; + +std::unique_ptr concatenate_with_nullifying_rows(table_view const& input, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) +{ + // Generate offsets of the output lists column. + auto [list_offsets, list_validities] = generate_list_offsets_and_validities(input, stream, mr); + auto const offsets_view = list_offsets->view(); + + // Copy entries from the input lists columns to the output lists column - this needed to be + // specialized for different types. + auto const num_output_lists = input.num_rows(); + auto const num_output_entries = + cudf::detail::get_value(offsets_view, num_output_lists, stream); + auto list_entries = + type_dispatcher(lists_column_view(*input.begin()).child().type(), + concatenate_lists_fn{}, + input, + offsets_view, + num_output_lists, + num_output_entries, + stream, + mr); + + auto [null_mask, null_count] = cudf::detail::valid_if( + list_validities.begin(), list_validities.end(), thrust::identity{}, stream, mr); + return make_lists_column(num_output_lists, + std::move(list_offsets), + std::move(list_entries), + null_count, + null_count ? std::move(null_mask) : rmm::device_buffer{}, + stream, + mr); +} + +} // namespace + +/** + * @copydoc cudf::lists::concatenate_rows + * + * @param stream CUDA stream used for device memory operations and kernel launches. + */ +std::unique_ptr concatenate_rows(table_view const& input, + concatenate_null_policy null_policy, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) +{ + CUDF_EXPECTS(input.num_columns() > 0, "The input table must have at least one column."); + + auto const entry_type = lists_column_view(*input.begin()).child().type(); + for (auto const& col : input) { + CUDF_EXPECTS(col.type().id() == type_id::LIST, + "All columns of the input table must be of lists column type."); + + auto const child_col = lists_column_view(col).child(); + CUDF_EXPECTS(not cudf::is_nested(child_col.type()), "Nested types are not supported."); + CUDF_EXPECTS(entry_type == child_col.type(), + "The types of entries in the input columns must be the same."); + } + + if (input.num_rows() == 0) { return cudf::empty_like(input.column(0)); } + if (input.num_columns() == 1) { return std::make_unique(*(input.begin()), stream, mr); } + + // List concatenation can be implemented by simply interleaving the lists columns, then modify the + // list offsets. + auto const has_null_mask = std::any_of( + std::cbegin(input), std::cend(input), [](auto const& col) { return col.nullable(); }); + if (not has_null_mask or null_policy == concatenate_null_policy::IGNORE) { + return concatenate_rows_ignore_null(input, has_null_mask, stream, mr); + } + + // Both conditions satisfied: has_null_mask == true and + // null_policy == NULLIFY_OUTPUT_ROW. + return concatenate_with_nullifying_rows(input, stream, mr); +} + +} // namespace detail + +/** + * @copydoc cudf::lists::concatenate_rows + */ +std::unique_ptr concatenate_rows(table_view const& lists_columns, + concatenate_null_policy null_policy, + rmm::mr::device_memory_resource* mr) +{ + CUDF_FUNC_RANGE(); + return detail::concatenate_rows(lists_columns, null_policy, rmm::cuda_stream_default, mr); +} + +} // namespace lists +} // namespace cudf diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 7a3e14b4f12..6dd50592274 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -397,10 +397,11 @@ ConfigureTest(AST_TEST ast/transform_tests.cpp) ################################################################################################### # - lists tests ---------------------------------------------------------------------------------- ConfigureTest(LISTS_TEST + lists/concatenate_rows_tests.cpp lists/contains_tests.cpp lists/count_elements_tests.cpp - lists/explode_tests.cpp lists/drop_list_duplicates_tests.cpp + lists/explode_tests.cpp lists/extract_tests.cpp lists/sort_lists_tests.cpp) diff --git a/cpp/tests/lists/concatenate_rows_tests.cpp b/cpp/tests/lists/concatenate_rows_tests.cpp new file mode 100644 index 00000000000..9c4329677e1 --- /dev/null +++ b/cpp/tests/lists/concatenate_rows_tests.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include + +namespace { +using StrListsCol = cudf::test::lists_column_wrapper; +using IntListsCol = cudf::test::lists_column_wrapper; +using IntCol = cudf::test::fixed_width_column_wrapper; +using TView = cudf::table_view; + +constexpr bool print_all{false}; // For debugging +constexpr int32_t null{0}; + +auto all_nulls() { return cudf::test::iterator_all_nulls(); } + +auto null_at(cudf::size_type idx) { return cudf::test::iterator_with_null_at(idx); } + +auto null_at(std::vector const& indices) +{ + return cudf::test::iterator_with_null_at(cudf::host_span{indices}); +} + +} // namespace + +struct ListConcatenateRowsTest : public cudf::test::BaseFixture { +}; + +TEST_F(ListConcatenateRowsTest, InvalidInput) +{ + // Empty input table + EXPECT_THROW(cudf::lists::concatenate_rows(TView{}), cudf::logic_error); + + // Input table contains non-list column + { + auto const col1 = IntCol{}.release(); + auto const col2 = IntListsCol{}.release(); + EXPECT_THROW(cudf::lists::concatenate_rows(TView{{col1->view(), col2->view()}}), + cudf::logic_error); + } + + // Types mismatch + { + auto const col1 = IntListsCol{}.release(); + auto const col2 = StrListsCol{}.release(); + EXPECT_THROW(cudf::lists::concatenate_rows(TView{{col1->view(), col2->view()}}), + cudf::logic_error); + } + + // Nested types are not supported + { + auto const col = IntListsCol{{IntListsCol{1, 2, 3}, IntListsCol{4, 5, 6}}}.release(); + EXPECT_THROW(cudf::lists::concatenate_rows(TView{{col->view(), col->view()}}), + cudf::logic_error); + } +} + +template +struct ListConcatenateRowsTypedTest : public cudf::test::BaseFixture { +}; + +using TypesForTest = cudf::test::Concat; +TYPED_TEST_CASE(ListConcatenateRowsTypedTest, TypesForTest); + +TYPED_TEST(ListConcatenateRowsTypedTest, ConcatenateEmptyColumns) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col = ListsCol{}.release(); + auto const results = cudf::lists::concatenate_rows(TView{{col->view(), col->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*col, *results, print_all); +} + +TYPED_TEST(ListConcatenateRowsTypedTest, ConcatenateOneColumnNotNull) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col = ListsCol{{1, 2}, {3, 4}, {5, 6}}.release(); + auto const results = cudf::lists::concatenate_rows(TView{{col->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*col, *results, print_all); +} + +TYPED_TEST(ListConcatenateRowsTypedTest, ConcatenateOneColumnWithNulls) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col = ListsCol{{ListsCol{{1, 2, null}, null_at(2)}, + ListsCol{} /*NULL*/, + ListsCol{{null, 3, 4, 4, 4, 4}, null_at(0)}, + ListsCol{5, 6}}, + null_at(1)} + .release(); + auto const results = cudf::lists::concatenate_rows(TView{{col->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*col, *results, print_all); +} + +TYPED_TEST(ListConcatenateRowsTypedTest, SimpleInputNoNull) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col1 = ListsCol{{1, 2}, {3, 4}, {5, 6}}.release(); + auto const col2 = ListsCol{{7, 8}, {9, 10}, {11, 12}}.release(); + auto const expected = ListsCol{{1, 2, 7, 8}, {3, 4, 9, 10}, {5, 6, 11, 12}}.release(); + auto const results = cudf::lists::concatenate_rows(TView{{col1->view(), col2->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + +TEST_F(ListConcatenateRowsTest, SimpleInputStringsColumnsNoNull) +{ + auto const col1 = StrListsCol{ + StrListsCol{"Tomato", "Apple"}, + StrListsCol{"Banana", "Kiwi", "Cherry"}, + StrListsCol{ + "Coconut"}}.release(); + auto const col2 = + StrListsCol{StrListsCol{"Orange"}, StrListsCol{"Lemon", "Peach"}, StrListsCol{}}.release(); + auto const expected = StrListsCol{ + StrListsCol{"Tomato", "Apple", "Orange"}, + StrListsCol{"Banana", "Kiwi", "Cherry", "Lemon", "Peach"}, + StrListsCol{ + "Coconut"}}.release(); + auto const results = cudf::lists::concatenate_rows(TView{{col1->view(), col2->view()}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + +TYPED_TEST(ListConcatenateRowsTypedTest, SimpleInputWithNulls) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col1 = ListsCol{{ListsCol{{1, null, 3, 4}, null_at(1)}, + ListsCol{{null, 2, 3, 4}, null_at(0)}, + ListsCol{{null, 2, 3, 4}, null_at(0)}, + ListsCol{} /*NULL*/, + ListsCol{{1, 2, null, 4}, null_at(2)}, + ListsCol{{1, 2, 3, null}, null_at(3)}}, + null_at(3)} + .release(); + auto const col2 = ListsCol{{ListsCol{{10, 11, 12, null}, null_at(3)}, + ListsCol{{13, 14, 15, 16, 17, null}, null_at(5)}, + ListsCol{} /*NULL*/, + ListsCol{{null, 18}, null_at(0)}, + ListsCol{{19, 20, null}, null_at(2)}, + ListsCol{{null}, null_at(0)}}, + null_at(2)} + .release(); + auto const col3 = ListsCol{{ListsCol{} /*NULL*/, + ListsCol{{20, null}, null_at(1)}, + ListsCol{{null, 21, null, null}, null_at({0, 2, 3})}, + ListsCol{}, + ListsCol{22, 23, 24, 25}, + ListsCol{{null, null, null, null, null}, all_nulls()}}, + null_at(0)} + .release(); + + // Ignore null list elements + { + auto const results = + cudf::lists::concatenate_rows(TView{{col1->view(), col2->view(), col3->view()}}); + auto const expected = + ListsCol{ListsCol{{1, null, 3, 4, 10, 11, 12, null}, null_at({1, 7})}, + ListsCol{{null, 2, 3, 4, 13, 14, 15, 16, 17, null, 20, null}, null_at({0, 9, 11})}, + ListsCol{{null, 2, 3, 4, null, 21, null, null}, null_at({0, 4, 6, 7})}, + ListsCol{{null, 18}, null_at(0)}, + ListsCol{{1, 2, null, 4, 19, 20, null, 22, 23, 24, 25}, null_at({2, 6})}, + ListsCol{{1, 2, 3, null, null, null, null, null, null, null}, + null_at({3, 4, 5, 6, 7, 8, 9})}} + .release(); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); + } + + // Null list rows result in null list rows + { + auto const results = + cudf::lists::concatenate_rows(TView{{col1->view(), col2->view(), col3->view()}}, + cudf::lists::concatenate_null_policy::NULLIFY_OUTPUT_ROW); + auto const expected = + ListsCol{{ListsCol{} /*NULL*/, + ListsCol{{null, 2, 3, 4, 13, 14, 15, 16, 17, null, 20, null}, null_at({0, 9, 11})}, + ListsCol{} /*NULL*/, + ListsCol{} /*NULL*/, + ListsCol{{1, 2, null, 4, 19, 20, null, 22, 23, 24, 25}, null_at({2, 6})}, + ListsCol{{1, 2, 3, null, null, null, null, null, null, null}, + null_at({3, 4, 5, 6, 7, 8, 9})}}, + null_at({0, 2, 3})} + .release(); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); + } +} + +TEST_F(ListConcatenateRowsTest, SimpleInputStringsColumnsWithNulls) +{ + auto const col1 = StrListsCol{ + StrListsCol{{"Tomato", "Bear" /*NULL*/, "Apple"}, null_at(1)}, + StrListsCol{{"Banana", "Pig" /*NULL*/, "Kiwi", "Cherry", "Whale" /*NULL*/}, null_at({1, 4})}, + StrListsCol{ + "Coconut"}}.release(); + auto const col2 = + StrListsCol{ + {StrListsCol{{"Orange", "Dog" /*NULL*/, "Fox" /*NULL*/, "Duck" /*NULL*/}, null_at({1, 2, 3})}, + StrListsCol{"Lemon", "Peach"}, + StrListsCol{{"Deer" /*NULL*/, "Snake" /*NULL*/, "Horse" /*NULL*/}, all_nulls()}}, /*NULL*/ + null_at(2)} + .release(); + + // Ignore null list elements + { + auto const results = cudf::lists::concatenate_rows(TView{{col1->view(), col2->view()}}); + auto const expected = StrListsCol{ + StrListsCol{{"Tomato", "" /*NULL*/, "Apple", "Orange", "" /*NULL*/, "" /*NULL*/, "" /*NULL*/}, + null_at({1, 4, 5, 6})}, + StrListsCol{{"Banana", "" /*NULL*/, "Kiwi", "Cherry", "" /*NULL*/, "Lemon", "Peach"}, + null_at({1, 4})}, + StrListsCol{ + "Coconut"}}.release(); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); + } + + // Null list rows result in null list rows + { + auto const results = + cudf::lists::concatenate_rows(TView{{col1->view(), col2->view()}}, + cudf::lists::concatenate_null_policy::NULLIFY_OUTPUT_ROW); + auto const expected = + StrListsCol{ + {StrListsCol{ + {"Tomato", "" /*NULL*/, "Apple", "Orange", "" /*NULL*/, "" /*NULL*/, "" /*NULL*/}, + null_at({1, 4, 5, 6})}, + StrListsCol{{"Banana", "" /*NULL*/, "Kiwi", "Cherry", "" /*NULL*/, "Lemon", "Peach"}, + null_at({1, 4})}, + StrListsCol{""} /*NULL*/}, + null_at(2)} + .release(); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); + } +} + +TYPED_TEST(ListConcatenateRowsTypedTest, SlicedColumnsInputNoNull) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col_original = ListsCol{{1, 2, 3}, {2, 3}, {3, 4, 5, 6}, {5, 6}, {}, {7}}.release(); + auto const col1 = cudf::slice(col_original->view(), {0, 3})[0]; + auto const col2 = cudf::slice(col_original->view(), {1, 4})[0]; + auto const col3 = cudf::slice(col_original->view(), {2, 5})[0]; + auto const col4 = cudf::slice(col_original->view(), {3, 6})[0]; + auto const expected = ListsCol{ + {1, 2, 3, 2, 3, 3, 4, 5, 6, 5, 6}, + {2, 3, 3, 4, 5, 6, 5, 6}, + {3, 4, 5, 6, 5, 6, 7}}.release(); + auto const results = cudf::lists::concatenate_rows(TView{{col1, col2, col3, col4}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + +TYPED_TEST(ListConcatenateRowsTypedTest, SlicedColumnsInputWithNulls) +{ + using ListsCol = cudf::test::lists_column_wrapper; + + auto const col_original = ListsCol{{ListsCol{{null, 2, 3}, null_at(0)}, + ListsCol{2, 3}, /*NULL*/ + ListsCol{{3, null, 5, 6}, null_at(1)}, + ListsCol{5, 6}, /*NULL*/ + ListsCol{}, /*NULL*/ + ListsCol{7}, + ListsCol{8, 9, 10}}, + null_at({1, 3, 4})} + .release(); + auto const col1 = cudf::slice(col_original->view(), {0, 3})[0]; + auto const col2 = cudf::slice(col_original->view(), {1, 4})[0]; + auto const col3 = cudf::slice(col_original->view(), {2, 5})[0]; + auto const col4 = cudf::slice(col_original->view(), {3, 6})[0]; + auto const col5 = cudf::slice(col_original->view(), {4, 7})[0]; + auto const expected = ListsCol{ + ListsCol{{null, 2, 3, 3, null, 5, 6}, null_at({0, 4})}, + ListsCol{{3, null, 5, 6, 7}, null_at(1)}, + ListsCol{{3, null, 5, 6, 7, 8, 9, 10}, + null_at(1)}}.release(); + auto const results = cudf::lists::concatenate_rows(TView{{col1, col2, col3, col4, col5}}); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); +} + +TEST_F(ListConcatenateRowsTest, SlicedStringsColumnsInputWithNulls) +{ + auto const col = + StrListsCol{ + {StrListsCol{{"Tomato", "Bear" /*NULL*/, "Apple"}, null_at(1)}, + StrListsCol{{"Banana", "Pig" /*NULL*/, "Kiwi", "Cherry", "Whale" /*NULL*/}, null_at({1, 4})}, + StrListsCol{"Coconut"}, + StrListsCol{{"Orange", "Dog" /*NULL*/, "Fox" /*NULL*/, "Duck" /*NULL*/}, null_at({1, 2, 3})}, + StrListsCol{"Lemon", "Peach"}, + StrListsCol{{"Deer" /*NULL*/, "Snake" /*NULL*/, "Horse" /*NULL*/}, all_nulls()}}, /*NULL*/ + null_at(5)} + .release(); + auto const col1 = cudf::slice(col->view(), {0, 3})[0]; + auto const col2 = cudf::slice(col->view(), {1, 4})[0]; + auto const col3 = cudf::slice(col->view(), {2, 5})[0]; + auto const col4 = cudf::slice(col->view(), {3, 6})[0]; + + { + auto const results = cudf::lists::concatenate_rows(TView{{col1, col2, col3, col4}}); + auto const expected = StrListsCol{ + StrListsCol{{"Tomato", + "" /*NULL*/, + "Apple", + "Banana", + "" /*NULL*/, + "Kiwi", + "Cherry", + "" /*NULL*/, + "Coconut", + "Orange", + "" /*NULL*/, + "" /*NULL*/, + "" /*NULL*/}, + null_at({1, 4, 7, 10, 11, 12})}, + StrListsCol{{"Banana", + "" /*NULL*/, + "Kiwi", + "Cherry", + "" /*NULL*/, + "Coconut", + "Orange", + "" /*NULL*/, + "" /*NULL*/, + "", /*NULL*/ + "Lemon", + "Peach"}, + null_at({1, 4, 7, 8, 9})}, + StrListsCol{ + { + "Coconut", + "Orange", + "" /*NULL*/, + "" /*NULL*/, + "", /*NULL*/ + "Lemon", + "Peach", + }, + null_at({2, 3, 4})}}.release(); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); + } + + { + auto const results = cudf::lists::concatenate_rows( + TView{{col1, col2, col3, col4}}, cudf::lists::concatenate_null_policy::NULLIFY_OUTPUT_ROW); + auto const expected = StrListsCol{{StrListsCol{{"Tomato", + "" /*NULL*/, + "Apple", + "Banana", + "" /*NULL*/, + "Kiwi", + "Cherry", + "" /*NULL*/, + "Coconut", + "Orange", + "" /*NULL*/, + "" /*NULL*/, + "" /*NULL*/}, + null_at({1, 4, 7, 10, 11, 12})}, + StrListsCol{{"Banana", + "" /*NULL*/, + "Kiwi", + "Cherry", + "" /*NULL*/, + "Coconut", + "Orange", + "" /*NULL*/, + "" /*NULL*/, + "", /*NULL*/ + "Lemon", + "Peach"}, + null_at({1, 4, 7, 8, 9})}, + StrListsCol{} /*NULL*/}, + null_at(2)} + .release(); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*expected, *results, print_all); + } +} From 5d50cdefe3d44e4db4d4a7af620468cb9c2499ad Mon Sep 17 00:00:00 2001 From: MithunR Date: Mon, 3 May 2021 15:18:20 -0700 Subject: [PATCH 24/65] Extend LEAD/LAG to work with non-fixed-width types (#8062) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit extends the [LEAD()/LAG() offset window functions](https://learnsql.com/blog/lead-and-lag-functions-in-sql/) introduced in #6277 to work with more than just fixed-width data types. The functions are defined as follows: 1. `LEAD(N)`: Returns the row from the input column, at the specified offset past the current row. If the offset crosses the grouping boundary or column boundary for a given row, a "default" value is returned if available. The "default" value is null, by default. 1. `LAG(N)`: returns the row from the input column at the specified offset preceding the current row. If the offset crosses the grouping boundary or column boundary for a given row, a "default" value is returned if available. The "default" value is null, by default. As an illustration, consider the following example input array input column, with two groups (`G1` and `G2`): ```c++ [ [1,1,1], [2], [3,3,3], [4,4,4,4], [66,66], [], [88,88], [99] ] <--------------G1--------------> <------------G2-----------> ``` `LEAD(col, 1)` yields: ```c++ [ [2], [3,3,3], [4,4,4,4], ∅, [], [88,88], [99], ∅ ] ``` `LAG(input_col, 2)` yields: ```c++ [ ∅, ∅, [2], [3,3,3], ∅, ∅, [], [88,88] ] ``` If a `defaults` column is specified with contents: ```c++ [ [999], [999], [999], [999], [999], [999], [999], [999] ] ``` then, `LEAD(col, 1, defaults)` yields: ```c++ [ [2], [3,3,3], [4,4,4,4], [999], [], [88,88], [99], [999] ] ``` Note that in the cases where the offset (`1`) would cross the column/group boundary (i.e. indices `3` and `7`), the corresponding entry from the `defaults` column is returned instead of `∅` (i.e. `null`). Authors: - MithunR (https://github.com/mythrocks) Approvers: - Nghia Truong (https://github.com/ttnghia) - Ram (Ramakrishna Prabhu) (https://github.com/rgsl888prabhu) - Karthikeyan (https://github.com/karthikeyann) URL: https://github.com/rapidsai/cudf/pull/8062 --- cpp/src/rolling/lead_lag_nested_detail.cuh | 228 ++++++++ cpp/src/rolling/rolling_detail.cuh | 72 ++- cpp/tests/rolling/lead_lag_test.cpp | 601 ++++++++++++++++++++- 3 files changed, 843 insertions(+), 58 deletions(-) create mode 100644 cpp/src/rolling/lead_lag_nested_detail.cuh diff --git a/cpp/src/rolling/lead_lag_nested_detail.cuh b/cpp/src/rolling/lead_lag_nested_detail.cuh new file mode 100644 index 00000000000..a202626fc24 --- /dev/null +++ b/cpp/src/rolling/lead_lag_nested_detail.cuh @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cudf::detail { +namespace { +/** + * @brief Functor to calculate the gather map used for calculating LEAD/LAG. + * + * @tparam op Aggregation Kind (LEAD vs LAG) + * @tparam PrecedingIterator Iterator to retrieve preceding window bounds + * @tparam FollowingIterator Iterator to retrieve following window bounds + */ +template +class lead_lag_gather_map_builder { + public: + lead_lag_gather_map_builder(size_type input_size, + size_type row_offset, + PrecedingIterator preceding, + FollowingIterator following) + : _input_size{input_size}, + _null_index{input_size}, // Out of input range. Gather returns null. + _row_offset{row_offset}, + _preceding{preceding}, + _following{following} + { + } + + template + size_type __device__ operator()(size_type i) + { + // Note: grouped_*rolling_window() trims preceding/following to + // the beginning/end of the group. `rolling_window()` does not. + // Must trim _following[i] so as not to go past the column end. + auto following = min(_following[i], _input_size - i - 1); + return (_row_offset > following) ? _null_index : (i + _row_offset); + } + + template + size_type __device__ operator()(size_type i) + { + // Note: grouped_*rolling_window() trims preceding/following to + // the beginning/end of the group. `rolling_window()` does not. + // Must trim _preceding[i] so as not to go past the column start. + auto preceding = min(_preceding[i], i + 1); + return (_row_offset > (preceding - 1)) ? _null_index : (i - _row_offset); + } + + private: + size_type const _input_size; // Number of rows in input to LEAD/LAG. + size_type const _null_index; // Index value to use to output NULL for LEAD/LAG calculation. + size_type const _row_offset; // LEAD/LAG offset. E.g. For LEAD(2), _row_offset == 2. + PrecedingIterator _preceding; // Iterator to retrieve preceding window offset. + FollowingIterator _following; // Iterator to retrieve following window offset. +}; + +/** + * @brief Predicate to find indices at which LEAD/LAG evaluated to null. + */ +template +class is_null_index_predicate_impl { + public: + is_null_index_predicate_impl(size_type input_size, GatherMapIter gather_) + : _null_index{input_size}, _gather{gather_} + { + } + + bool __device__ operator()(size_type i) const { return _gather[i] == _null_index; } + + private: + size_type const _null_index; // Index value to use to output NULL for LEAD/LAG calculation. + GatherMapIter _gather; // Iterator for gather-map entries. +}; + +/** + * @brief Helper to construct is_null_index_predicate_impl + */ +template +is_null_index_predicate_impl is_null_index_predicate(size_type input_size, + GatherMapIter gather) +{ + return is_null_index_predicate_impl{input_size, gather}; +} + +} // namespace + +/** + * @brief Helper function to calculate LEAD/LAG for nested-type input columns. + * + * @tparam op The sort of aggregation being done (LEAD vs LAG) + * @tparam InputType The datatype of the input column being aggregated + * @tparam PrecedingIterator Iterator-type that returns the preceding bounds + * @tparam FollowingIterator Iterator-type that returns the following bounds + * @param[in] input Nested-type input column for LEAD/LAG calculation + * @param[in] default_outputs Default values to use as outputs, if LEAD/LAG + * offset crosses column/group boundaries + * @param[in] preceding Iterator to retrieve preceding window bounds + * @param[in] following Iterator to retrieve following window bounds + * @param[in] offset Lead/Lag offset, indicating which row after/before + * the current row is to be returned + * @param[in] stream CUDA stream for device memory operations/allocations + * @param[in] mr device_memory_resource for device memory allocations + */ +template ())> +std::unique_ptr compute_lead_lag_for_nested(column_view const& input, + column_view const& default_outputs, + PrecedingIter preceding, + FollowingIter following, + size_type offset, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) +{ + CUDF_EXPECTS(default_outputs.type().id() == input.type().id(), + "Defaults column type must match input column."); // Because LEAD/LAG. + + CUDF_EXPECTS(default_outputs.is_empty() || (input.size() == default_outputs.size()), + "Number of defaults must match input column."); + + // For LEAD(0)/LAG(0), no computation need be performed. + // Return copy of input. + if (offset == 0) { return std::make_unique(input, stream, mr); } + + // Algorithm: + // + // 1. Construct gather_map with the LEAD/LAG offset applied to the indices. + // E.g. A gather_map of: + // {0, 1, 2, 3, ..., N-3, N-2, N-1} + // would select the input column, unchanged. + // + // For LEAD(2), the following gather_map is used: + // {3, 4, 5, 6, ..., N-1, NULL_INDEX, NULL_INDEX} + // where `NULL_INDEX` selects `NULL` for the gather. + // + // Similarly, LAG(2) is implemented using the following gather_map: + // {NULL_INDEX, NULL_INDEX, 0, 1, 2...} + // + // 2. Gather input column based on the gather_map. + // 3. If default outputs are available, scatter contents of `default_outputs` + // to all positions where nulls where gathered in step 2. + // + // Note: Step 3 can be switched to use `copy_if_else()`, once it supports + // nested types. + + auto static constexpr size_data_type = data_type{type_to_id()}; + + auto gather_map_column = + make_numeric_column(size_data_type, input.size(), mask_state::UNALLOCATED, stream); + auto gather_map = gather_map_column->mutable_view(); + + thrust::transform(rmm::exec_policy(stream), + thrust::make_counting_iterator(size_type{0}), + thrust::make_counting_iterator(size_type{input.size()}), + gather_map.begin(), + lead_lag_gather_map_builder{ + input.size(), offset, preceding, following}); + + auto output_with_nulls = + cudf::detail::gather(table_view{std::vector{input}}, + gather_map_column->view().template begin(), + gather_map_column->view().end(), + out_of_bounds_policy::NULLIFY, + stream, + mr); + + if (default_outputs.is_empty()) { return std::move(output_with_nulls->release()[0]); } + + // Must scatter defaults. + auto scatter_map = rmm::device_uvector(input.size(), stream); + + // Find all indices at which LEAD/LAG computed nulls previously. + auto scatter_map_end = + thrust::copy_if(rmm::exec_policy(stream), + thrust::make_counting_iterator(size_type{0}), + thrust::make_counting_iterator(size_type{input.size()}), + scatter_map.begin(), + is_null_index_predicate(input.size(), gather_map.begin())); + + // Bail early, if all LEAD/LAG computations succeeded. No defaults need be substituted. + if (scatter_map.is_empty()) { return std::move(output_with_nulls->release()[0]); } + + // Gather only those default values that are to be substituted. + auto gathered_defaults = + cudf::detail::gather(table_view{std::vector{default_outputs}}, + scatter_map.begin(), + scatter_map_end, + out_of_bounds_policy::DONT_CHECK, + stream); + + // Scatter defaults into locations where LEAD/LAG computed nulls. + auto scattered_results = cudf::detail::scatter( + table_view{std::vector{gathered_defaults->release()[0]->view()}}, + scatter_map.begin(), + scatter_map_end, + table_view{std::vector{output_with_nulls->release()[0]->view()}}, + false, + stream, + mr); + return std::move(scattered_results->release()[0]); +} + +} // namespace cudf::detail diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 4b60c5aec0c..a26dc4c7120 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -16,6 +16,7 @@ #pragma once +#include "lead_lag_nested_detail.cuh" #include "rolling_detail.hpp" #include @@ -744,23 +745,14 @@ struct rolling_window_launcher { std::unique_ptr> launch(column_view const& input, column_view const& default_outputs, - PrecedingWindowIterator preceding_window_begin, - FollowingWindowIterator following_window_begin, + PrecedingWindowIterator preceding, + FollowingWindowIterator following, size_type min_periods, std::unique_ptr const& agg, agg_op const& device_agg_op, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { - CUDF_EXPECTS(default_outputs.type().id() == input.type().id(), - "Defaults column type must match input column."); // Because LEAD/LAG. - - // For LEAD(0)/LAG(0), no computation need be performed. - // Return copy of input. - if (0 == static_cast(agg.get())->row_offset) { - return std::make_unique(input, stream, mr); - } - auto output = make_fixed_width_column( target_type(input.type(), op), input.size(), mask_state::UNINITIALIZED, stream, mr); @@ -770,8 +762,8 @@ struct rolling_window_launcher { input, default_outputs, output_view, - preceding_window_begin, - following_window_begin, + preceding, + following, min_periods, agg, device_agg_op, @@ -782,30 +774,6 @@ struct rolling_window_launcher { return output; } - // Deals with invalid column and/or aggregation options - template - std::enable_if_t(), - std::unique_ptr> - launch(column_view const& input, - column_view const& default_outputs, - PrecedingWindowIterator preceding_window_begin, - FollowingWindowIterator following_window_begin, - size_type min_periods, - std::unique_ptr const& agg, - agg_op device_agg_op, - rmm::cuda_stream_view stream, - rmm::mr::device_memory_resource* mr) - { - CUDF_FAIL( - "Aggregation operator and/or input type combination is invalid: " - "LEAD/LAG supported only on fixed-width types"); - } - template @@ -866,7 +834,9 @@ struct rolling_window_launcher { template - std::enable_if_t<(op == aggregation::LEAD || op == aggregation::LAG), std::unique_ptr> + std::enable_if_t() && + (op == aggregation::LEAD || op == aggregation::LAG), + std::unique_ptr> operator()(column_view const& input, column_view const& default_outputs, PrecedingWindowIterator preceding_window_begin, @@ -892,6 +862,32 @@ struct rolling_window_launcher { mr); } + template + std::enable_if_t() && + (op == aggregation::LEAD || op == aggregation::LAG), + std::unique_ptr> + operator()(column_view const& input, + column_view const& default_outputs, + PrecedingWindowIterator preceding_window_begin, + FollowingWindowIterator following_window_begin, + size_type min_periods, + std::unique_ptr const& agg, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + { + return cudf::detail:: + compute_lead_lag_for_nested( + input, + default_outputs, + preceding_window_begin, + following_window_begin, + static_cast(agg.get())->row_offset, + stream, + mr); + } + /** * @brief Creates the offsets child of the result of the `COLLECT_LIST` window aggregation * diff --git a/cpp/tests/rolling/lead_lag_test.cpp b/cpp/tests/rolling/lead_lag_test.cpp index 1cf7d74285c..bc71a7acab9 100644 --- a/cpp/tests/rolling/lead_lag_test.cpp +++ b/cpp/tests/rolling/lead_lag_test.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include @@ -323,7 +325,6 @@ TYPED_TEST(TypedLeadLagWindowTest, TestLeadLagWithZeroOffsets) following, min_periods, cudf::make_lag_aggregation(0)); - ; expect_columns_equivalent(*lag_0_output_col, *input_col); } @@ -354,7 +355,6 @@ TYPED_TEST(TypedLeadLagWindowTest, TestLeadLagWithNegativeOffsets) following, min_periods, cudf::make_lag_aggregation(-3)); - ; expect_columns_equivalent( *lag_minus_3_output_col, @@ -404,7 +404,6 @@ TYPED_TEST(TypedLeadLagWindowTest, TestLeadLagWithNoGrouping) following, min_periods, cudf::make_lead_aggregation(3)); - ; expect_columns_equivalent( *lead_3_output_col, @@ -513,31 +512,593 @@ TYPED_TEST(TypedLeadLagWindowTest, DefaultValuesWithoutLeadLag) aggs.begin(), aggs.end(), [&](auto& agg) { assert_aggregation_fails(std::move(agg)); }); } -TEST_F(LeadLagWindowTest, LeadLagWithoutFixedWidthInput) +template +struct TypedNestedLeadLagWindowTest : public cudf::test::BaseFixture { +}; + +TYPED_TEST_CASE(TypedNestedLeadLagWindowTest, TypesForTest); + +TYPED_TEST(TypedNestedLeadLagWindowTest, NumericListsWithNullsAllOver) { - // Check that Lead/Lag aren't supported for non-fixed-width types. + using T = TypeParam; + using lcw = lists_column_wrapper; + + auto null_at_2 = cudf::test::iterator_with_null_at(2); + auto const input_col = lcw{{{0, 0}, + {1, 1}, + {2, 2}, + {3, 3, 3}, + {{4, 4, 4, 4}, null_at_2}, + {5, 5, 5, 5, 5}, + {0, 0}, + {10, 10}, + {20, 20}, + {30, 30, 30}, + {40, 40, 40, 40}, + {{50, 50, 50, 50, 50}, null_at_2}}, + null_at_2} + .release(); - auto const input_col = strings_column_wrapper{ - {"0", "1", "2", "3", "4", "5"}, cudf::detail::make_counting_transform_iterator(0, [](auto i) { - return false; - })}.release(); + auto const grouping_key = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}; + auto const grouping_keys = cudf::table_view{std::vector{grouping_key}}; - auto const grouping_key = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0}; + auto const preceding = 4; + auto const following = 3; + auto const min_periods = 1; + + auto lead_3_output_col = cudf::grouped_rolling_window(grouping_keys, + input_col->view(), + preceding, + following, + min_periods, + cudf::make_lead_aggregation(3)); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT( + lead_3_output_col->view(), + lcw{{{3, 3, 3}, + {{4, 4, 4, 4}, null_at_2}, + {5, 5, 5, 5, 5}, + {}, + {}, + {}, + {30, 30, 30}, + {40, 40, 40, 40}, + {{50, 50, 50, 50, 50}, null_at_2}, + {}, + {}, + {}}, + iterator_with_null_at(std::vector{3, 4, 5, 9, 10, 11})} + .release() + ->view()); + + auto lag_1_output_col = cudf::grouped_rolling_window(grouping_keys, + input_col->view(), + preceding, + following, + min_periods, + cudf::make_lag_aggregation(1)); + + expect_columns_equivalent(lag_1_output_col->view(), + lcw{{{}, + {0, 0}, + {1, 1}, + {2, 2}, + {3, 3, 3}, + {{4, 4, 4, 4}, null_at_2}, + {}, + {0, 0}, + {10, 10}, + {20, 20}, + {30, 30, 30}, + {40, 40, 40, 40}}, + iterator_with_null_at(std::vector{0, 3, 6})} + .release() + ->view()); +} + +TYPED_TEST(TypedNestedLeadLagWindowTest, NumericListsWithDefaults) +{ + using T = TypeParam; + using lcw = lists_column_wrapper; + + auto null_at_2 = cudf::test::iterator_with_null_at(2); + auto const input_col = lcw{{{0, 0}, + {1, 1}, + {2, 2}, + {3, 3, 3}, + {{4, 4, 4, 4}, null_at_2}, + {5, 5, 5, 5, 5}, + {0, 0}, + {10, 10}, + {20, 20}, + {30, 30, 30}, + {40, 40, 40, 40}, + {{50, 50, 50, 50, 50}, null_at_2}}, + null_at_2} + .release(); + + auto const defaults_col = + lcw{ + { + {}, + {91, 91}, + {92, 92}, + {}, // null! + {94, 94, 94}, + {95, 95}, + {}, + {91, 91}, + {92, 92}, + {}, // null! + {94, 94, 94}, + {95, 95}, + }, + } + .release(); + + auto const grouping_key = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}; auto const grouping_keys = cudf::table_view{std::vector{grouping_key}}; - auto const default_value = cudf::make_string_scalar("99"); - auto const default_outputs = cudf::make_column_from_scalar(*default_value, input_col->size()); + auto const preceding = 4; + auto const following = 3; + auto const min_periods = 1; + + auto lead_3_output_col = cudf::grouped_rolling_window(grouping_keys, + input_col->view(), + preceding, + following, + min_periods, + cudf::make_lead_aggregation(3)); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT( + lead_3_output_col->view(), + lcw{{{3, 3, 3}, + {{4, 4, 4, 4}, null_at_2}, + {5, 5, 5, 5, 5}, + {}, + {}, + {}, + {30, 30, 30}, + {40, 40, 40, 40}, + {{50, 50, 50, 50, 50}, null_at_2}, + {}, + {}, + {}}, + iterator_with_null_at(std::vector{3, 4, 5, 9, 10, 11})} + .release() + ->view()); + + auto lag_1_output_col = cudf::grouped_rolling_window(grouping_keys, + input_col->view(), + preceding, + following, + min_periods, + cudf::make_lag_aggregation(1)); + + expect_columns_equivalent(lag_1_output_col->view(), + lcw{{{}, + {0, 0}, + {1, 1}, + {2, 2}, + {3, 3, 3}, + {{4, 4, 4, 4}, null_at_2}, + {}, + {0, 0}, + {10, 10}, + {20, 20}, + {30, 30, 30}, + {40, 40, 40, 40}}, + iterator_with_null_at(std::vector{0, 3, 6})} + .release() + ->view()); +} + +TYPED_TEST(TypedNestedLeadLagWindowTest, Structs) +{ + using T = TypeParam; + using lcw = lists_column_wrapper; + + auto null_at_2 = cudf::test::iterator_with_null_at(2); + auto lists_col = lcw{{{0, 0}, + {1, 1}, + {2, 2}, + {3, 3, 3}, + {{4, 4, 4, 4}, null_at_2}, + {5, 5, 5, 5, 5}, + {0, 0}, + {10, 10}, + {20, 20}, + {30, 30, 30}, + {40, 40, 40, 40}, + {{50, 50, 50, 50, 50}, null_at_2}}, + null_at_2}; + + auto strings_col = strings_column_wrapper{{"00", + "11", + "22", + "333", + "4444", + "55555", + "00", + "1010", + "2020", + "303030", + "40404040", + "5050505050"}, + iterator_with_null_at(9)}; + + auto structs_col = structs_column_wrapper{lists_col, strings_col}.release(); + + auto const grouping_key = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}; + auto const grouping_keys = cudf::table_view{std::vector{grouping_key}}; + + auto const preceding = 4; + auto const following = 3; + auto const min_periods = 1; + + // Test LEAD(). + { + auto lead_3_output_col = cudf::grouped_rolling_window(grouping_keys, + structs_col->view(), + preceding, + following, + min_periods, + cudf::make_lead_aggregation(3)); + auto expected_lists_col = + lcw{{{3, 3, 3}, + {{4, 4, 4, 4}, null_at_2}, + {5, 5, 5, 5, 5}, + {}, + {}, + {}, + {30, 30, 30}, + {40, 40, 40, 40}, + {{50, 50, 50, 50, 50}, null_at_2}, + {}, + {}, + {}}, + iterator_with_null_at(std::vector{3, 4, 5, 9, 10, 11})}; + auto expected_strings_col = strings_column_wrapper{ + {"333", "4444", "55555", "", "", "", "", "40404040", "5050505050", "", "", ""}, + iterator_with_null_at(std::vector{3, 4, 5, 6, 9, 10, 11})}; + + auto expected_structs_col = + structs_column_wrapper{{expected_lists_col, expected_strings_col}, + iterator_with_null_at(std::vector{3, 4, 5, 9, 10, 11})} + .release(); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(lead_3_output_col->view(), expected_structs_col->view()); + } + + // Test LAG() + { + auto lag_1_output_col = cudf::grouped_rolling_window(grouping_keys, + structs_col->view(), + preceding, + following, + min_periods, + cudf::make_lag_aggregation(1)); + auto expected_lists_col = lcw{{{}, // null. + {0, 0}, + {1, 1}, + {}, // null. + {3, 3, 3}, + {{4, 4, 4, 4}, null_at_2}, + {}, // null. + {0, 0}, + {10, 10}, + {20, 20}, + {30, 30, 30}, + {40, 40, 40, 40}}, + iterator_with_null_at(std::vector{0, 3, 6})}; + auto expected_strings_col = + strings_column_wrapper{{"", // null. + "00", + "11", + "22", + "333", + "4444", + "", // null. + "00", + "1010", + "2020", + "", // null. + "40404040"}, + iterator_with_null_at(std::vector{0, 6, 10})}; + + auto expected_structs_col = + structs_column_wrapper{{expected_lists_col, expected_strings_col}, + iterator_with_null_at(std::vector{0, 6})} + .release(); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(lag_1_output_col->view(), expected_structs_col->view()); + } +} + +struct LeadLagNonFixedWidthTest : cudf::test::BaseFixture { +}; + +TEST_F(LeadLagNonFixedWidthTest, StringsNoDefaults) +{ + using namespace cudf; + using namespace cudf::test; + + auto input_col = strings_column_wrapper{{"", + "A_1", + "A_22", + "A_333", + "A_4444", + "A_55555", + "B_0", + "", + "B_22", + "B_333", + "B_4444", + "B_55555"}, + iterator_with_null_at(std::vector{0, 7})} + .release(); + + auto const grouping_key = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}; + auto const grouping_keys = cudf::table_view{std::vector{grouping_key}}; + + auto const preceding = 4; + auto const following = 3; + auto const min_periods = 1; + + auto lead_2 = grouped_rolling_window(grouping_keys, + input_col->view(), + preceding, + following, + min_periods, + cudf::make_lead_aggregation(2)); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT( + lead_2->view(), + strings_column_wrapper{ + {"A_22", "A_333", "A_4444", "A_55555", "", "", "B_22", "B_333", "B_4444", "B_55555", "", ""}, + iterator_with_null_at(std::vector{4, 5, 10, 11})}); + + auto lag_1 = grouped_rolling_window(grouping_keys, + input_col->view(), + preceding, + following, + min_periods, + cudf::make_lag_aggregation(1)); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT( + lag_1->view(), + strings_column_wrapper{ + {"", "", "A_1", "A_22", "A_333", "A_4444", "", "B_0", "", "B_22", "B_333", "B_4444"}, + iterator_with_null_at(std::vector{0, 1, 6, 8})}); +} + +TEST_F(LeadLagNonFixedWidthTest, StringsWithDefaults) +{ + using namespace cudf; + using namespace cudf::test; + + auto input_col = strings_column_wrapper{{"", + "A_1", + "A_22", + "A_333", + "A_4444", + "A_55555", + "B_0", + "", + "B_22", + "B_333", + "B_4444", + "B_55555"}, + iterator_with_null_at(std::vector{0, 7})} + .release(); + + auto defaults_col = strings_column_wrapper{"9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999"} + .release(); + + auto const grouping_key = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}; + auto const grouping_keys = cudf::table_view{std::vector{grouping_key}}; + + auto const preceding = 4; + auto const following = 3; + auto const min_periods = 1; + + auto lead_2 = grouped_rolling_window(grouping_keys, + input_col->view(), + defaults_col->view(), + preceding, + following, + min_periods, + cudf::make_lead_aggregation(2)); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(lead_2->view(), + strings_column_wrapper{"A_22", + "A_333", + "A_4444", + "A_55555", + "9999", + "9999", + "B_22", + "B_333", + "B_4444", + "B_55555", + "9999", + "9999"}); + + auto lag_1 = grouped_rolling_window(grouping_keys, + input_col->view(), + defaults_col->view(), + preceding, + following, + min_periods, + cudf::make_lag_aggregation(1)); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT( + lag_1->view(), + strings_column_wrapper{ + {"9999", "", "A_1", "A_22", "A_333", "A_4444", "9999", "B_0", "", "B_22", "B_333", "B_4444"}, + iterator_with_null_at(std::vector{1, 8})}); +} + +TEST_F(LeadLagNonFixedWidthTest, StringsWithDefaultsNoGroups) +{ + using namespace cudf; + using namespace cudf::test; + + auto input_col = strings_column_wrapper{{"", + "A_1", + "A_22", + "A_333", + "A_4444", + "A_55555", + "B_0", + "", + "B_22", + "B_333", + "B_4444", + "B_55555"}, + iterator_with_null_at(std::vector{0, 7})} + .release(); + + auto defaults_col = strings_column_wrapper{"9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999", + "9999"} + .release(); + + auto const grouping_key = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + auto const grouping_keys = cudf::table_view{std::vector{grouping_key}}; + + auto const preceding = 4; + auto const following = 3; + auto const min_periods = 1; + + auto lead_2 = grouped_rolling_window(grouping_keys, + input_col->view(), + defaults_col->view(), + preceding, + following, + min_periods, + cudf::make_lead_aggregation(2)); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(lead_2->view(), + strings_column_wrapper{{"A_22", + "A_333", + "A_4444", + "A_55555", + "B_0", + "", + "B_22", + "B_333", + "B_4444", + "B_55555", + "9999", + "9999"}, + iterator_with_null_at(5)}); + + auto lag_1 = grouped_rolling_window(grouping_keys, + input_col->view(), + defaults_col->view(), + preceding, + following, + min_periods, + cudf::make_lag_aggregation(1)); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT( + lag_1->view(), + strings_column_wrapper{{"9999", + "", + "A_1", + "A_22", + "A_333", + "A_4444", + "A_55555", + "B_0", + "", + "B_22", + "B_333", + "B_4444"}, + iterator_with_null_at(std::vector{1, 8})}); +} + +TEST_F(LeadLagNonFixedWidthTest, Dictionary) +{ + using namespace cudf; + using namespace cudf::test; + + using dictionary = cudf::test::dictionary_column_wrapper; + + auto input_strings = std::initializer_list{"", + "A_1", + "A_22", + "A_333", + "A_4444", + "A_55555", + "B_0", + "", + "B_22", + "B_333", + "B_4444", + "B_55555"}; + auto input_col = dictionary{input_strings}.release(); + + auto const grouping_key = fixed_width_column_wrapper{0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}; + auto const grouping_keys = cudf::table_view{std::vector{grouping_key}}; auto const preceding = 4; auto const following = 3; auto const min_periods = 1; - EXPECT_THROW(cudf::grouped_rolling_window(grouping_keys, - input_col->view(), - default_outputs->view(), - preceding, - following, - min_periods, - cudf::make_lead_aggregation(4)), - cudf::logic_error); + { + auto lead_2 = grouped_rolling_window(grouping_keys, + input_col->view(), + preceding, + following, + min_periods, + cudf::make_lead_aggregation(2)); + + auto expected_keys = strings_column_wrapper{input_strings}.release(); + auto expected_values = + fixed_width_column_wrapper{{2, 3, 4, 5, 0, 0, 7, 8, 9, 10, 0, 0}, + iterator_with_null_at(std::vector{4, 5, 10, 11})} + .release(); + auto expected_output = + make_dictionary_column(expected_keys->view(), expected_values->view()).release(); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(lead_2->view(), expected_output->view()); + } + + { + auto lag_1 = grouped_rolling_window(grouping_keys, + input_col->view(), + preceding, + following, + min_periods, + cudf::make_lag_aggregation(1)); + + auto expected_keys = strings_column_wrapper{input_strings}.release(); + auto expected_values = + fixed_width_column_wrapper{{0, 0, 1, 2, 3, 4, 0, 6, 0, 7, 8, 9}, + iterator_with_null_at(std::vector{0, 6})} + .release(); + auto expected_output = + make_dictionary_column(expected_keys->view(), expected_values->view()).release(); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(lag_1->view(), expected_output->view()); + } } From 5b754ed561393e9784ba51a0114a3ff713967a3a Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Mon, 3 May 2021 19:13:32 -0500 Subject: [PATCH 25/65] Update io supported types docs page (#8146) `cudf` now supports `decimal64` and `struct` types in parquet reader & writer. `decimal64` is supported in `orc` reader too. Updated the docs IO supported types page to reflect the same. Authors: - GALI PREM SAGAR (https://github.com/galipremsagar) Approvers: - Keith Kraus (https://github.com/kkraus14) - Vukasin Milovanovic (https://github.com/vuule) URL: https://github.com/rapidsai/cudf/pull/8146 --- docs/cudf/source/io-supported-types.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cudf/source/io-supported-types.rst b/docs/cudf/source/io-supported-types.rst index e09e155ef92..ce2f7a85fbf 100644 --- a/docs/cudf/source/io-supported-types.rst +++ b/docs/cudf/source/io-supported-types.rst @@ -56,7 +56,7 @@ The following table lists are compatible cudf types for each supported IO format +-----------------------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ | datetime64[ns] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | +-----------------------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ - | struct | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | + | struct | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | +-----------------------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ - | decimal | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | + | decimal64 | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +-----------------------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ From bc9903a3295aefd39d63bf15b312752daa6645bc Mon Sep 17 00:00:00 2001 From: Karthikeyan <6488848+karthikeyann@users.noreply.github.com> Date: Tue, 4 May 2021 06:19:41 +0530 Subject: [PATCH 26/65] Add binary ops benchmark (#8008) closes https://github.com/rapidsai/cudf/issues/4118 Authors: - Karthikeyan (https://github.com/karthikeyann) Approvers: - Nghia Truong (https://github.com/ttnghia) - Ram (Ramakrishna Prabhu) (https://github.com/rgsl888prabhu) - Mark Harris (https://github.com/harrism) URL: https://github.com/rapidsai/cudf/pull/8008 --- cpp/benchmarks/CMakeLists.txt | 4 +- ...op_benchmark.cu => binaryop_benchmark.cpp} | 0 .../binaryop/jit_binaryop_benchmark.cpp | 90 +++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) rename cpp/benchmarks/binaryop/{binaryop_benchmark.cu => binaryop_benchmark.cpp} (100%) create mode 100644 cpp/benchmarks/binaryop/jit_binaryop_benchmark.cpp diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index 3aeeaf71edd..6fbd06cf981 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -182,7 +182,9 @@ ConfigureBench(AST_BENCH ast/transform_benchmark.cpp) ################################################################################################### # - binaryop benchmark ---------------------------------------------------------------------------- -ConfigureBench(BINARYOP_BENCH binaryop/binaryop_benchmark.cu) +ConfigureBench(BINARYOP_BENCH + binaryop/binaryop_benchmark.cpp + binaryop/jit_binaryop_benchmark.cpp) ################################################################################################### # - nvtext benchmark ------------------------------------------------------------------- diff --git a/cpp/benchmarks/binaryop/binaryop_benchmark.cu b/cpp/benchmarks/binaryop/binaryop_benchmark.cpp similarity index 100% rename from cpp/benchmarks/binaryop/binaryop_benchmark.cu rename to cpp/benchmarks/binaryop/binaryop_benchmark.cpp diff --git a/cpp/benchmarks/binaryop/jit_binaryop_benchmark.cpp b/cpp/benchmarks/binaryop/jit_binaryop_benchmark.cpp new file mode 100644 index 00000000000..29ca02a843d --- /dev/null +++ b/cpp/benchmarks/binaryop/jit_binaryop_benchmark.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include + +#include + +template +class JIT_BINARYOP : public cudf::benchmark { +}; + +template +void BM_binaryop(benchmark::State& state, cudf::binary_operator binop) +{ + const cudf::size_type column_size{(cudf::size_type)state.range(0)}; + + auto data_it = thrust::make_counting_iterator(0); + cudf::test::fixed_width_column_wrapper input1(data_it, data_it + column_size); + cudf::test::fixed_width_column_wrapper input2(data_it, data_it + column_size); + + auto lhs = cudf::column_view(input1); + auto rhs = cudf::column_view(input2); + auto output_dtype = cudf::data_type(cudf::type_to_id()); + + // Call once for hot cache. + cudf::binary_operation(lhs, rhs, binop, output_dtype); + + for (auto _ : state) { + cuda_event_timer timer(state, true); + cudf::binary_operation(lhs, rhs, binop, output_dtype); + } +} + +// TODO tparam boolean for null. +#define BINARYOP_BENCHMARK_DEFINE(TypeLhs, TypeRhs, binop, TypeOut) \ + BENCHMARK_TEMPLATE_DEFINE_F(JIT_BINARYOP, binop, TypeLhs, TypeRhs, TypeOut) \ + (::benchmark::State & st) \ + { \ + BM_binaryop(st, cudf::binary_operator::binop); \ + } \ + BENCHMARK_REGISTER_F(JIT_BINARYOP, binop) \ + ->Unit(benchmark::kMillisecond) \ + ->UseManualTime() \ + ->Arg(10000) /* 10k */ \ + ->Arg(100000) /* 100k */ \ + ->Arg(1000000) /* 1M */ \ + ->Arg(10000000) /* 10M */ \ + ->Arg(100000000); /* 100M */ + +using namespace cudf; + +// clang-format off +BINARYOP_BENCHMARK_DEFINE(float, int64_t, ADD, int32_t); +BINARYOP_BENCHMARK_DEFINE(duration_s, duration_D, SUB, duration_ms); +BINARYOP_BENCHMARK_DEFINE(float, float, MUL, int64_t); +BINARYOP_BENCHMARK_DEFINE(int64_t, int64_t, DIV, int64_t); +BINARYOP_BENCHMARK_DEFINE(int64_t, int64_t, TRUE_DIV, int64_t); +BINARYOP_BENCHMARK_DEFINE(int64_t, int64_t, FLOOR_DIV, int64_t); +BINARYOP_BENCHMARK_DEFINE(double, double, MOD, double); +BINARYOP_BENCHMARK_DEFINE(int64_t, int64_t, POW, double); +BINARYOP_BENCHMARK_DEFINE(int64_t, int32_t, BITWISE_AND, int16_t); +BINARYOP_BENCHMARK_DEFINE(int16_t, int32_t, BITWISE_OR, int64_t); +BINARYOP_BENCHMARK_DEFINE(int16_t, int64_t, BITWISE_XOR, int32_t); +BINARYOP_BENCHMARK_DEFINE(double, int8_t, LOGICAL_AND, int16_t); +BINARYOP_BENCHMARK_DEFINE(int16_t, int64_t, LOGICAL_OR, bool); +BINARYOP_BENCHMARK_DEFINE(timestamp_s, timestamp_s, LESS, bool); +BINARYOP_BENCHMARK_DEFINE(timestamp_ms, timestamp_s, GREATER, bool); +BINARYOP_BENCHMARK_DEFINE(int, int, SHIFT_LEFT, int); +BINARYOP_BENCHMARK_DEFINE(int16_t, int64_t, SHIFT_RIGHT, int); +BINARYOP_BENCHMARK_DEFINE(int64_t, int32_t, SHIFT_RIGHT_UNSIGNED, int64_t); +BINARYOP_BENCHMARK_DEFINE(int32_t, int64_t, PMOD, double); +BINARYOP_BENCHMARK_DEFINE(float, double, ATAN2, double); From 7f3799c4885bca7523e77d6844d53512a0e6403c Mon Sep 17 00:00:00 2001 From: Robert Maynard Date: Tue, 4 May 2021 08:08:54 -0400 Subject: [PATCH 27/65] convert replace/clamp.cu to use optional iterator (#8004) Replace the usage of the `pair_iterator` with `optional_iterator` as it better expresses the state of the scalar values. Authors: - Robert Maynard (https://github.com/robertmaynard) Approvers: - Vukasin Milovanovic (https://github.com/vuule) URL: https://github.com/rapidsai/cudf/pull/8004 --- cpp/benchmarks/CMakeLists.txt | 5 + cpp/benchmarks/replace/clamp_benchmark.cpp | 78 +++++++++++++ cpp/src/replace/clamp.cu | 122 +++++++++------------ 3 files changed, 137 insertions(+), 68 deletions(-) create mode 100644 cpp/benchmarks/replace/clamp_benchmark.cpp diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index 6fbd06cf981..27897143e7b 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -124,6 +124,11 @@ ConfigureBench(REDUCTION_BENCH reduction/scan_benchmark.cpp reduction/minmax_benchmark.cpp) +################################################################################################### +# - reduction benchmark --------------------------------------------------------------------------- +ConfigureBench(REPLACE_BENCH + replace/clamp_benchmark.cpp) + ################################################################################################### # - filling benchmark ----------------------------------------------------------------------------- ConfigureBench(FILL_BENCH diff --git a/cpp/benchmarks/replace/clamp_benchmark.cpp b/cpp/benchmarks/replace/clamp_benchmark.cpp new file mode 100644 index 00000000000..f897b9d82cc --- /dev/null +++ b/cpp/benchmarks/replace/clamp_benchmark.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +class ReplaceClamp : public cudf::benchmark { +}; + +template +static void BM_reduction_scan(benchmark::State& state, bool include_nulls) +{ + cudf::size_type const n_rows{(cudf::size_type)state.range(0)}; + auto const dtype = cudf::type_to_id(); + auto const table = create_random_table({dtype}, 1, row_count{n_rows}); + if (!include_nulls) { table->get_column(0).set_null_mask(rmm::device_buffer{}, 0); } + cudf::column_view input(table->view().column(0)); + + auto [low_scalar, high_scalar] = cudf::minmax(input); + + // set the clamps 2 in from the min and max + { + using ScalarType = cudf::scalar_type_t; + auto lvalue = static_cast(low_scalar.get()); + auto hvalue = static_cast(high_scalar.get()); + + // super heavy clamp + auto mid = lvalue->value() + (hvalue->value() - lvalue->value()) / 2; + lvalue->set_value(mid - 10); + hvalue->set_value(mid + 10); + } + + for (auto _ : state) { + cuda_event_timer timer(state, true); + auto result = cudf::clamp(input, *low_scalar, *high_scalar); + } +} + +#define CLAMP_BENCHMARK_DEFINE(name, type, nulls) \ + BENCHMARK_DEFINE_F(ReplaceClamp, name) \ + (::benchmark::State & state) { BM_reduction_scan(state, nulls); } \ + BENCHMARK_REGISTER_F(ReplaceClamp, name) \ + ->UseManualTime() \ + ->Arg(10000) /* 10k */ \ + ->Arg(100000) /* 100k */ \ + ->Arg(1000000) /* 1M */ \ + ->Arg(10000000) /* 10M */ \ + ->Arg(100000000); /* 100M */ + +CLAMP_BENCHMARK_DEFINE(int8_no_nulls, int8_t, false); +CLAMP_BENCHMARK_DEFINE(int32_no_nulls, int32_t, false); +CLAMP_BENCHMARK_DEFINE(uint64_no_nulls, uint64_t, false); +CLAMP_BENCHMARK_DEFINE(float_no_nulls, float, false); +CLAMP_BENCHMARK_DEFINE(int16_nulls, int16_t, true); +CLAMP_BENCHMARK_DEFINE(uint32_nulls, uint32_t, true); +CLAMP_BENCHMARK_DEFINE(double_nulls, double, true); diff --git a/cpp/src/replace/clamp.cu b/cpp/src/replace/clamp.cu index 84151bbce0e..e0e54570cd6 100644 --- a/cpp/src/replace/clamp.cu +++ b/cpp/src/replace/clamp.cu @@ -74,12 +74,12 @@ std::pair, std::unique_ptr> form_offsets_and_cha return std::make_pair(std::move(offsets_column), std::move(chars_column)); } -template +template std::unique_ptr clamp_string_column(strings_column_view const& input, - ScalarIterator const& lo_itr, - ScalarIterator const& lo_replace_itr, - ScalarIterator const& hi_itr, - ScalarIterator const& hi_replace_itr, + OptionalScalarIterator const& lo_itr, + ReplaceScalarIterator const& lo_replace_itr, + OptionalScalarIterator const& hi_itr, + ReplaceScalarIterator const& hi_replace_itr, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { @@ -90,18 +90,16 @@ std::unique_ptr clamp_string_column(strings_column_view const& inp // build offset column auto offsets_transformer = [lo_itr, hi_itr, lo_replace_itr, hi_replace_itr] __device__( string_view element, bool is_valid = true) { - const auto d_lo = (*lo_itr).first; - const auto d_hi = (*hi_itr).first; - const auto d_lo_replace = (*lo_replace_itr).first; - const auto d_hi_replace = (*hi_replace_itr).first; - const auto lo_valid = (*lo_itr).second; - const auto hi_valid = (*hi_itr).second; + const auto d_lo = (*lo_itr).value_or(element); + const auto d_hi = (*hi_itr).value_or(element); + const auto d_lo_replace = *(*lo_replace_itr); + const auto d_hi_replace = *(*hi_replace_itr); size_type bytes = 0; if (is_valid) { - if (lo_valid and element < d_lo) { + if (element < d_lo) { bytes = d_lo_replace.size_bytes(); - } else if (hi_valid and d_hi < element) { + } else if (d_hi < element) { bytes = d_hi_replace.size_bytes(); } else { bytes = element.size_bytes(); @@ -123,16 +121,14 @@ std::unique_ptr clamp_string_column(strings_column_view const& inp size_type idx) { if (d_input.is_null(idx)) { return; } auto input_element = d_input.element(idx); - const auto d_lo = (*lo_itr).first; - const auto d_hi = (*hi_itr).first; - const auto d_lo_replace = (*lo_replace_itr).first; - const auto d_hi_replace = (*hi_replace_itr).first; - const auto lo_valid = (*lo_itr).second; - const auto hi_valid = (*hi_itr).second; - - if (lo_valid and input_element < d_lo) { + const auto d_lo = (*lo_itr).value_or(input_element); + const auto d_hi = (*hi_itr).value_or(input_element); + const auto d_lo_replace = *(*lo_replace_itr); + const auto d_hi_replace = *(*hi_replace_itr); + + if (input_element < d_lo) { memcpy(d_chars + d_offsets[idx], d_lo_replace.data(), d_lo_replace.size_bytes()); - } else if (hi_valid and d_hi < input_element) { + } else if (d_hi < input_element) { memcpy(d_chars + d_offsets[idx], d_hi_replace.data(), d_hi_replace.size_bytes()); } else { memcpy(d_chars + d_offsets[idx], input_element.data(), input_element.size_bytes()); @@ -153,13 +149,13 @@ std::unique_ptr clamp_string_column(strings_column_view const& inp mr); } -template +template std::enable_if_t(), std::unique_ptr> clamper( column_view const& input, - ScalarIterator const& lo_itr, - ScalarIterator const& lo_replace_itr, - ScalarIterator const& hi_itr, - ScalarIterator const& hi_replace_itr, + OptionalScalarIterator const& lo_itr, + ReplaceScalarIterator const& lo_replace_itr, + OptionalScalarIterator const& hi_itr, + ReplaceScalarIterator const& hi_replace_itr, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { @@ -174,49 +170,39 @@ std::enable_if_t(), std::unique_ptr> clamp auto scalar_zip_itr = thrust::make_zip_iterator(thrust::make_tuple(lo_itr, lo_replace_itr, hi_itr, hi_replace_itr)); - auto trans = [] __device__(auto element_validity_pair, auto scalar_tuple) { - if (element_validity_pair.second) { - auto lo_validity_pair = thrust::get<0>(scalar_tuple); - auto hi_validity_pair = thrust::get<2>(scalar_tuple); - if (lo_validity_pair.second and (element_validity_pair.first < lo_validity_pair.first)) { - return thrust::get<1>(scalar_tuple).first; - } else if (hi_validity_pair.second and - (element_validity_pair.first > hi_validity_pair.first)) { - return thrust::get<3>(scalar_tuple).first; + auto trans = [] __device__(auto element_optional, auto scalar_tuple) { + if (element_optional.has_value()) { + auto lo_optional = thrust::get<0>(scalar_tuple); + auto hi_optional = thrust::get<2>(scalar_tuple); + if (lo_optional.has_value() and (*element_optional < *lo_optional)) { + return *(thrust::get<1>(scalar_tuple)); + } else if (hi_optional.has_value() and (*element_optional > *hi_optional)) { + return *(thrust::get<3>(scalar_tuple)); } } - return element_validity_pair.first; + return *element_optional; }; - if (input.has_nulls()) { - auto input_pair_iterator = make_pair_iterator(*input_device_view); - thrust::transform(rmm::exec_policy(stream), - input_pair_iterator, - input_pair_iterator + input.size(), - scalar_zip_itr, - output_device_view->begin(), - trans); - } else { - auto input_pair_iterator = make_pair_iterator(*input_device_view); - thrust::transform(rmm::exec_policy(stream), - input_pair_iterator, - input_pair_iterator + input.size(), - scalar_zip_itr, - output_device_view->begin(), - trans); - } + auto input_pair_iterator = + make_optional_iterator(*input_device_view, contains_nulls::DYNAMIC{}, input.has_nulls()); + thrust::transform(rmm::exec_policy(stream), + input_pair_iterator, + input_pair_iterator + input.size(), + scalar_zip_itr, + output_device_view->begin(), + trans); return output; } -template +template std::enable_if_t::value, std::unique_ptr> clamper( column_view const& input, - ScalarIterator const& lo_itr, - ScalarIterator const& lo_replace_itr, - ScalarIterator const& hi_itr, - ScalarIterator const& hi_replace_itr, + OptionalScalarIterator const& lo_itr, + ReplaceScalarIterator const& lo_replace_itr, + OptionalScalarIterator const& hi_itr, + ReplaceScalarIterator const& hi_replace_itr, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { @@ -225,13 +211,13 @@ std::enable_if_t::value, std::unique_ptr +template std::unique_ptr clamp( column_view const& input, - ScalarIterator const& lo_itr, - ScalarIterator const& lo_replace_itr, - ScalarIterator const& hi_itr, - ScalarIterator const& hi_replace_itr, + OptionalScalarIterator const& lo_itr, + ReplaceScalarIterator const& lo_replace_itr, + OptionalScalarIterator const& hi_itr, + ReplaceScalarIterator const& hi_replace_itr, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) { @@ -251,10 +237,10 @@ struct dispatch_clamp { { CUDF_EXPECTS(lo.type() == input.type(), "mismatching types of scalar and input"); - auto lo_itr = make_pair_iterator(lo); - auto hi_itr = make_pair_iterator(hi); - auto lo_replace_itr = make_pair_iterator(lo_replace); - auto hi_replace_itr = make_pair_iterator(hi_replace); + auto lo_itr = make_optional_iterator(lo, contains_nulls::YES{}); + auto hi_itr = make_optional_iterator(hi, contains_nulls::YES{}); + auto lo_replace_itr = make_optional_iterator(lo_replace, contains_nulls::NO{}); + auto hi_replace_itr = make_optional_iterator(hi_replace, contains_nulls::NO{}); return clamp(input, lo_itr, lo_replace_itr, hi_itr, hi_replace_itr, stream, mr); } From be2a1ed8ed9c85919d14f23bcf51c2b2235e2d98 Mon Sep 17 00:00:00 2001 From: David Wendt <45795991+davidwendt@users.noreply.github.com> Date: Tue, 4 May 2021 08:47:01 -0400 Subject: [PATCH 28/65] Fix groupby reduce_functor for fixed-point result-type (#8127) The following error was found with a libcudf debug build when running its GROUPBY_TEST ``` [ RUN ] groupby_argmin_test/21.basic GROUPBY_TEST: ../include/cudf/types.hpp:267: cudf::data_type::data_type(cudf::type_id, int32_t): Assertion `id == type_id::DECIMAL32 || id == type_id::DECIMAL64' failed. ``` This PR corrects the logic error causing this assert. The assert only occurs with a debug build so the failure was hidden but apparently benign since the `data_type` is still created properly. Authors: - David Wendt (https://github.com/davidwendt) Approvers: - Conor Hoekstra (https://github.com/codereport) - Nghia Truong (https://github.com/ttnghia) URL: https://github.com/rapidsai/cudf/pull/8127 --- cpp/src/groupby/sort/group_single_pass_reduction_util.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/groupby/sort/group_single_pass_reduction_util.cuh b/cpp/src/groupby/sort/group_single_pass_reduction_util.cuh index e5e93bbef47..67062658c39 100644 --- a/cpp/src/groupby/sort/group_single_pass_reduction_util.cuh +++ b/cpp/src/groupby/sort/group_single_pass_reduction_util.cuh @@ -65,7 +65,7 @@ struct reduce_functor { using OpType = cudf::detail::corresponding_operator_t; using ResultType = cudf::detail::target_type_t; - auto result_type = is_fixed_point() + auto result_type = is_fixed_point() ? data_type{type_to_id(), values.type().scale()} : data_type{type_to_id()}; From d56428abfc7345e2f7be3b679f2383e0c1eb7084 Mon Sep 17 00:00:00 2001 From: ChrisJar Date: Tue, 4 May 2021 08:42:36 -0500 Subject: [PATCH 29/65] Enable concat for decimal columns with mixed precision and scale (#8099) This enables concat between decimal columns with different precision and scale by finding the smallest necessary scale and precision that can encapsulate all of the columns to be concatenated. It calculates this using the same logic as the `sum` operation. Closes #8080 Authors: - https://github.com/ChrisJar Approvers: - GALI PREM SAGAR (https://github.com/galipremsagar) - https://github.com/brandon-b-miller URL: https://github.com/rapidsai/cudf/pull/8099 --- python/cudf/cudf/core/frame.py | 16 +++++++++++ python/cudf/cudf/core/series.py | 10 ++++++- python/cudf/cudf/tests/test_concat.py | 40 +++++++++++++++++++++++++++ python/cudf/cudf/utils/dtypes.py | 9 ++++++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/python/cudf/cudf/core/frame.py b/python/cudf/cudf/core/frame.py index 4a434be42ce..29bee0a6543 100644 --- a/python/cudf/cudf/core/frame.py +++ b/python/cudf/cudf/core/frame.py @@ -486,6 +486,11 @@ def _concat( cudf.core.index.as_index(out.index._values) ) + # Reassign precision for any decimal cols + for name, col in out._data.items(): + if isinstance(col, cudf.core.column.DecimalColumn): + col = tables[0]._data[name]._copy_type_metadata(col) + # Reassign index and column names if isinstance(objs[0].columns, pd.MultiIndex): out.columns = objs[0].columns @@ -3723,6 +3728,17 @@ def _find_common_dtypes_and_categories(non_null_columns, dtypes): # Set the column dtype to the codes' dtype. The categories # will be re-assigned at the end dtypes[idx] = min_scalar_type(len(categories[idx])) + elif all( + isinstance(col, cudf.core.column.DecimalColumn) for col in cols + ): + # Find the largest scale and the largest difference between + # precision and scale of the columns to be concatenated + s = max([col.dtype.scale for col in cols]) + lhs = max([col.dtype.precision - col.dtype.scale for col in cols]) + # Combine to get the necessary precision and clip at the maximum + # precision + p = min(cudf.Decimal64Dtype.MAX_PRECISION, s + lhs) + dtypes[idx] = cudf.Decimal64Dtype(p, s) # Otherwise raise an error if columns have different dtypes elif not all(is_dtype_equal(c.dtype, dtypes[idx]) for c in cols): raise ValueError("All columns must be the same type") diff --git a/python/cudf/cudf/core/series.py b/python/cudf/cudf/core/series.py index 5ee40d576b6..c896f0ea8f6 100644 --- a/python/cudf/cudf/core/series.py +++ b/python/cudf/cudf/core/series.py @@ -54,6 +54,7 @@ is_scalar, min_scalar_type, numeric_normalize_types, + _decimal_normalize_types, ) from cudf.utils.utils import ( get_appropriate_dispatched_func, @@ -2585,9 +2586,16 @@ def _concat(cls, objs, axis=0, index=True): ) if dtype_mismatch: - objs = numeric_normalize_types(*objs) + if isinstance(objs[0]._column, cudf.core.column.DecimalColumn): + objs = _decimal_normalize_types(*objs) + else: + objs = numeric_normalize_types(*objs) col = ColumnBase._concat([o._column for o in objs]) + + if isinstance(col, cudf.core.column.DecimalColumn): + col = objs[0]._column._copy_type_metadata(col) + return cls(data=col, index=index, name=name) @property diff --git a/python/cudf/cudf/tests/test_concat.py b/python/cudf/cudf/tests/test_concat.py index 9d5efb0be48..31dc6012905 100644 --- a/python/cudf/cudf/tests/test_concat.py +++ b/python/cudf/cudf/tests/test_concat.py @@ -9,6 +9,7 @@ import cudf as gd from cudf.tests.utils import assert_eq, assert_exceptions_equal from cudf.utils.dtypes import is_categorical_dtype +from cudf.core.dtypes import Decimal64Dtype def make_frames(index=None, nulls="none"): @@ -1222,3 +1223,42 @@ def test_concat_single_object(ignore_index, typ): """Ensure that concat on a single object does not change it.""" obj = typ([1, 2, 3]) assert_eq(gd.concat([obj], ignore_index=ignore_index, axis=0), obj) + + +@pytest.mark.parametrize("ltype", [Decimal64Dtype(3, 1), Decimal64Dtype(7, 2)]) +@pytest.mark.parametrize("rtype", [Decimal64Dtype(3, 2), Decimal64Dtype(8, 4)]) +def test_concat_decimal_dataframe(ltype, rtype): + gdf1 = gd.DataFrame( + {"id": np.random.randint(0, 10, 3), "val": ["22.3", "59.5", "81.1"]} + ) + gdf2 = gd.DataFrame( + {"id": np.random.randint(0, 10, 3), "val": ["2.35", "5.59", "8.14"]} + ) + + gdf1["val"] = gdf1["val"].astype(ltype) + gdf2["val"] = gdf2["val"].astype(rtype) + + pdf1 = gdf1.to_pandas() + pdf2 = gdf2.to_pandas() + + got = gd.concat([gdf1, gdf2]) + expected = pd.concat([pdf1, pdf2]) + + assert_eq(expected, got) + + +@pytest.mark.parametrize("ltype", [Decimal64Dtype(4, 1), Decimal64Dtype(8, 2)]) +@pytest.mark.parametrize( + "rtype", [Decimal64Dtype(4, 3), Decimal64Dtype(10, 4)] +) +def test_concat_decimal_series(ltype, rtype): + gs1 = gd.Series(["228.3", "559.5", "281.1"]).astype(ltype) + gs2 = gd.Series(["2.345", "5.259", "8.154"]).astype(rtype) + + ps1 = gs1.to_pandas() + ps2 = gs2.to_pandas() + + got = gd.concat([gs1, gs2]) + expected = pd.concat([ps1, ps2]) + + assert_eq(expected, got) diff --git a/python/cudf/cudf/utils/dtypes.py b/python/cudf/cudf/utils/dtypes.py index 363e05175c0..16c35bab4b1 100644 --- a/python/cudf/cudf/utils/dtypes.py +++ b/python/cudf/cudf/utils/dtypes.py @@ -290,6 +290,15 @@ def is_decimal_dtype(obj): ) +def _decimal_normalize_types(*args): + s = max([a.dtype.scale for a in args]) + lhs = max([a.dtype.precision - a.dtype.scale for a in args]) + p = min(cudf.Decimal64Dtype.MAX_PRECISION, s + lhs) + dtype = cudf.Decimal64Dtype(p, s) + + return [a.astype(dtype) for a in args] + + def cudf_dtype_from_pydata_dtype(dtype): """ Given a numpy or pandas dtype, converts it into the equivalent cuDF Python dtype. From 770dc385154853b1dc1c7ccbe676442e81414f8a Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Tue, 4 May 2021 14:31:06 -0500 Subject: [PATCH 30/65] Fix `factorize` doc string (#8154) Fixes: https://github.com/rapidsai/cudf/issues/7922 This PR fixes the docstring of `factorize` APIs where the return types were incorrect. Authors: - GALI PREM SAGAR (https://github.com/galipremsagar) Approvers: - Keith Kraus (https://github.com/kkraus14) URL: https://github.com/rapidsai/cudf/pull/8154 --- python/cudf/cudf/core/algorithms.py | 11 +++-------- python/cudf/cudf/core/series.py | 14 ++++---------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/python/cudf/cudf/core/algorithms.py b/python/cudf/cudf/core/algorithms.py index af908a664dc..9f26ac8ee78 100644 --- a/python/cudf/cudf/core/algorithms.py +++ b/python/cudf/cudf/core/algorithms.py @@ -18,7 +18,7 @@ def factorize(values, sort=False, na_sentinel=-1, size_hint=None): Returns -------- - (labels, cats) : (Series, Series) + (labels, cats) : (cupy.ndarray, cupy.ndarray or Index) - *labels* contains the encoded values - *cats* contains the categories in order that the N-th item corresponds to the (N-1) code. @@ -29,14 +29,9 @@ def factorize(values, sort=False, na_sentinel=-1, size_hint=None): >>> data = cudf.Series(['a', 'c', 'c']) >>> codes, uniques = cudf.factorize(data) >>> codes - 0 0 - 1 1 - 2 1 - dtype: int8 + array([0, 1, 1], dtype=int8) >>> uniques - 0 a - 1 c - dtype: object + StringIndex(['a' 'c'], dtype='object') See Also -------- diff --git a/python/cudf/cudf/core/series.py b/python/cudf/cudf/core/series.py index c896f0ea8f6..847dd5bc7f0 100644 --- a/python/cudf/cudf/core/series.py +++ b/python/cudf/cudf/core/series.py @@ -46,6 +46,7 @@ from cudf.utils import cudautils, docutils, ioutils from cudf.utils.docutils import copy_docstring from cudf.utils.dtypes import ( + _decimal_normalize_types, can_convert_to_column, is_decimal_dtype, is_list_dtype, @@ -54,7 +55,6 @@ is_scalar, min_scalar_type, numeric_normalize_types, - _decimal_normalize_types, ) from cudf.utils.utils import ( get_appropriate_dispatched_func, @@ -4054,7 +4054,7 @@ def factorize(self, na_sentinel=-1): Returns -------- - (labels, cats) : (Series, Series) + (labels, cats) : (cupy.ndarray, cupy.ndarray or Index) - *labels* contains the encoded values - *cats* contains the categories in order that the N-th item corresponds to the (N-1) code. @@ -4063,16 +4063,10 @@ def factorize(self, na_sentinel=-1): -------- >>> import cudf >>> s = cudf.Series(['a', 'a', 'c']) - >>> codes, uniques = s.factorize() >>> codes - 0 0 - 1 0 - 2 1 - dtype: int8 + array([0, 0, 1], dtype=int8) >>> uniques - 0 a - 1 c - dtype: object + StringIndex(['a' 'c'], dtype='object') """ return cudf.core.algorithms.factorize(self, na_sentinel=na_sentinel) From 53e1c666ed4f30af396b3bdd3239549774458bc8 Mon Sep 17 00:00:00 2001 From: ChrisJar Date: Tue, 4 May 2021 16:23:55 -0500 Subject: [PATCH 31/65] Enable not equal for decimal columns (#8143) This enables the not equal operation for binary operations involving decimal columns. Authors: - https://github.com/ChrisJar Approvers: - GALI PREM SAGAR (https://github.com/galipremsagar) URL: https://github.com/rapidsai/cudf/pull/8143 --- python/cudf/cudf/core/column/decimal.py | 2 +- python/cudf/cudf/tests/test_binops.py | 75 +++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/python/cudf/cudf/core/column/decimal.py b/python/cudf/cudf/core/column/decimal.py index 2bbb4c684f3..4608770b3ef 100644 --- a/python/cudf/cudf/core/column/decimal.py +++ b/python/cudf/cudf/core/column/decimal.py @@ -83,7 +83,7 @@ def binary_operator(self, op, other, reflect=False): result.dtype.precision = _binop_precision( self.dtype, other.dtype, op ) - elif op in ("eq", "lt", "gt", "le", "ge"): + elif op in ("eq", "ne", "lt", "gt", "le", "ge"): if not isinstance( other, (DecimalColumn, cudf.core.column.NumericalColumn, cudf.Scalar), diff --git a/python/cudf/cudf/tests/test_binops.py b/python/cudf/cudf/tests/test_binops.py index f7184a5a998..1ce9c74fce9 100644 --- a/python/cudf/cudf/tests/test_binops.py +++ b/python/cudf/cudf/tests/test_binops.py @@ -1913,6 +1913,33 @@ def _decimal_series(input, dtype): [True, None], bool, ), + ( + operator.ne, + ["0.06", "0.42"], + cudf.Decimal64Dtype(scale=2, precision=3), + ["0.18", "0.42"], + cudf.Decimal64Dtype(scale=2, precision=3), + [True, False], + bool, + ), + ( + operator.ne, + ["1.33", "1.21"], + cudf.Decimal64Dtype(scale=2, precision=3), + ["0.1899", "1.21"], + cudf.Decimal64Dtype(scale=4, precision=5), + [True, False], + bool, + ), + ( + operator.ne, + ["300", None], + cudf.Decimal64Dtype(scale=-2, precision=3), + ["110", "5500"], + cudf.Decimal64Dtype(scale=-1, precision=4), + [True, None], + bool, + ), ( operator.lt, ["0.18", "0.42", "1.00"], @@ -2066,6 +2093,30 @@ def test_binops_decimal(args): cudf.Series([True, False, None], dtype=bool), cudf.Series([True, False, None], dtype=bool), ), + ( + operator.ne, + ["100", "42", "24", None], + cudf.Decimal64Dtype(scale=0, precision=3), + [100, 40, 24, 12], + cudf.Series([False, True, False, None], dtype=bool), + cudf.Series([False, True, False, None], dtype=bool), + ), + ( + operator.ne, + ["10.1", "88", "11", None], + cudf.Decimal64Dtype(scale=1, precision=3), + [10, 42, 11, 12], + cudf.Series([True, True, False, None], dtype=bool), + cudf.Series([True, True, False, None], dtype=bool), + ), + ( + operator.ne, + ["100.000", "42", "23.999", None], + cudf.Decimal64Dtype(scale=3, precision=6), + [100, 42, 24, 12], + cudf.Series([False, False, True, None], dtype=bool), + cudf.Series([False, False, True, None], dtype=bool), + ), ( operator.lt, ["100", "40", "28", None], @@ -2459,6 +2510,30 @@ def decimal_series(input, dtype): cudf.Series([True, False, None], dtype=bool), cudf.Series([True, False, None], dtype=bool), ), + ( + operator.ne, + ["100.00", "41", None], + cudf.Decimal64Dtype(scale=2, precision=5), + 100, + cudf.Series([False, True, None], dtype=bool), + cudf.Series([False, True, None], dtype=bool), + ), + ( + operator.ne, + ["100.123", "120.21", None], + cudf.Decimal64Dtype(scale=3, precision=6), + decimal.Decimal("100.123"), + cudf.Series([False, True, None], dtype=bool), + cudf.Series([False, True, None], dtype=bool), + ), + ( + operator.ne, + ["100.123", "41", "120.21", None], + cudf.Decimal64Dtype(scale=3, precision=6), + cudf.Scalar(decimal.Decimal("100.123")), + cudf.Series([False, True, True, None], dtype=bool), + cudf.Series([False, True, True, None], dtype=bool), + ), ( operator.gt, ["100.00", "41", "120.21", None], From a80dff284048e65100997bf1bd0f5d3d20aab8e9 Mon Sep 17 00:00:00 2001 From: MithunR Date: Tue, 4 May 2021 16:42:57 -0700 Subject: [PATCH 32/65] Fix scatter output size for structs. (#8155) Fixes #8150, which causes CUDF failures if the `scatter()` source struct column has a different size from that of the target. It turns out that the implementation was erroneously using the source column's size to construct the result, instead of the target's. The following changes were made in this commit: 1. Scatter output's size was set to target's size instead of source. 2. Test case for the above. 3. Peripherally, `scatter_lists_test` and `scatter_struct_test` were moved from `.cu` to `.cpp`, for faster compile. Authors: - MithunR (https://github.com/mythrocks) Approvers: - Nghia Truong (https://github.com/ttnghia) - David Wendt (https://github.com/davidwendt) URL: https://github.com/rapidsai/cudf/pull/8155 --- cpp/include/cudf/detail/scatter.cuh | 2 +- cpp/tests/CMakeLists.txt | 4 ++-- ...r_list_tests.cu => scatter_list_tests.cpp} | 2 -- ...ruct_tests.cu => scatter_struct_tests.cpp} | 21 +++++++++++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) rename cpp/tests/copying/{scatter_list_tests.cu => scatter_list_tests.cpp} (99%) rename cpp/tests/copying/{scatter_struct_tests.cu => scatter_struct_tests.cpp} (93%) diff --git a/cpp/include/cudf/detail/scatter.cuh b/cpp/include/cudf/detail/scatter.cuh index d069ed06cae..68f4adfadc9 100644 --- a/cpp/include/cudf/detail/scatter.cuh +++ b/cpp/include/cudf/detail/scatter.cuh @@ -317,7 +317,7 @@ struct column_scatterer_impl { // Need to put the result column in a vector to call `gather_bitmask` std::vector> result; - result.emplace_back(cudf::make_structs_column(source.size(), + result.emplace_back(cudf::make_structs_column(target.size(), std::move(output_struct_members), 0, rmm::device_buffer{0, stream, mr}, diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 6dd50592274..29dd4319bfc 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -206,8 +206,8 @@ ConfigureTest(COPYING_TEST copying/pack_tests.cu copying/sample_tests.cpp copying/scatter_tests.cpp - copying/scatter_list_tests.cu - copying/scatter_struct_tests.cu + copying/scatter_list_tests.cpp + copying/scatter_struct_tests.cpp copying/segmented_gather_list_tests.cpp copying/shift_tests.cpp copying/slice_tests.cpp diff --git a/cpp/tests/copying/scatter_list_tests.cu b/cpp/tests/copying/scatter_list_tests.cpp similarity index 99% rename from cpp/tests/copying/scatter_list_tests.cu rename to cpp/tests/copying/scatter_list_tests.cpp index 92f1d44c46f..64fdf6d00d5 100644 --- a/cpp/tests/copying/scatter_list_tests.cu +++ b/cpp/tests/copying/scatter_list_tests.cpp @@ -18,8 +18,6 @@ #include #include -#include -#include #include #include #include diff --git a/cpp/tests/copying/scatter_struct_tests.cu b/cpp/tests/copying/scatter_struct_tests.cpp similarity index 93% rename from cpp/tests/copying/scatter_struct_tests.cu rename to cpp/tests/copying/scatter_struct_tests.cpp index a9bb1980d53..3993664168b 100644 --- a/cpp/tests/copying/scatter_struct_tests.cu +++ b/cpp/tests/copying/scatter_struct_tests.cpp @@ -291,3 +291,24 @@ TYPED_TEST(TypedStructScatterTest, ScatterStructOfListsTest) auto const scatter_map = int32s_col{-3, -2, -1, 5, 4, 3, 2}.release(); test_scatter(structs_src, structs_tgt, structs_expected, scatter_map); } + +TYPED_TEST(TypedStructScatterTest, ScatterSourceSmallerThanTarget) +{ + using namespace cudf; + using namespace cudf::test; + + using fixed_width = fixed_width_column_wrapper; + using structs_col = structs_column_wrapper; + using scatter_map_col = fixed_width_column_wrapper; + + auto source_child = fixed_width{22, 55}; + auto target_child = fixed_width{0, 1, 2, 3, 4, 5, 6}; + auto expected_child = fixed_width{0, 1, 22, 3, 4, 55, 6}; + + auto const source = structs_col{{source_child}}.release(); + auto const target = structs_col{{target_child}}.release(); + auto const scatter_map = scatter_map_col{2, 5}.release(); + auto const expected = structs_col{{expected_child}}.release(); + + test_scatter(source, target, expected, scatter_map); +} From 44f21b367ab2a351b87b2e6e65db6de64990cb92 Mon Sep 17 00:00:00 2001 From: GALI PREM SAGAR Date: Tue, 4 May 2021 18:47:07 -0500 Subject: [PATCH 33/65] Add `decimal64Dtype` support in data-generator (#8107) This PR adds `decimal64Dtype` support in the data-generator for the purpose of fuzz-testing. The changes also enable fuzz testing in parquet as this dtype is currently supported in parquet reader and writer only. Authors: - GALI PREM SAGAR (https://github.com/galipremsagar) Approvers: - Vukasin Milovanovic (https://github.com/vuule) - Keith Kraus (https://github.com/kkraus14) URL: https://github.com/rapidsai/cudf/pull/8107 --- python/cudf/cudf/_fuzz_testing/parquet.py | 4 +- python/cudf/cudf/_fuzz_testing/utils.py | 2 + python/cudf/cudf/tests/dataset_generator.py | 54 +++++++++++++++++---- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/python/cudf/cudf/_fuzz_testing/parquet.py b/python/cudf/cudf/_fuzz_testing/parquet.py index 4a9b63cd6aa..8c63b12d972 100644 --- a/python/cudf/cudf/_fuzz_testing/parquet.py +++ b/python/cudf/cudf/_fuzz_testing/parquet.py @@ -57,8 +57,8 @@ def generate_input(self): # TODO: Remove uint32 below after this bug is fixed # https://github.com/pandas-dev/pandas/issues/37327 - {"uint32"} + | {"list", "decimal64"} ) - dtypes_list.extend(["list"]) dtypes_meta, num_rows, num_cols = _generate_rand_meta( self, dtypes_list ) @@ -145,8 +145,8 @@ def generate_input(self): # TODO: Remove uint32 below after this bug is fixed # https://github.com/pandas-dev/pandas/issues/37327 - {"uint32"} + | {"list", "decimal64"} ) - dtypes_list.extend(["list"]) dtypes_meta, num_rows, num_cols = _generate_rand_meta( self, dtypes_list ) diff --git a/python/cudf/cudf/_fuzz_testing/utils.py b/python/cudf/cudf/_fuzz_testing/utils.py index efcbd8ca792..71b5a35a225 100644 --- a/python/cudf/cudf/_fuzz_testing/utils.py +++ b/python/cudf/cudf/_fuzz_testing/utils.py @@ -114,6 +114,8 @@ def _generate_rand_meta(obj, dtypes_list, null_frequency_override=None): meta["value_type"] = random.choice( list(cudf.utils.dtypes.ALL_TYPES - {"category"}) ) + elif dtype == "decimal64": + meta["max_precision"] = cudf.Decimal64Dtype.MAX_PRECISION meta["dtype"] = dtype meta["null_frequency"] = null_frequency diff --git a/python/cudf/cudf/tests/dataset_generator.py b/python/cudf/cudf/tests/dataset_generator.py index d3124d72a54..d7adf175f3f 100644 --- a/python/cudf/cudf/tests/dataset_generator.py +++ b/python/cudf/cudf/tests/dataset_generator.py @@ -131,15 +131,17 @@ def _generate_column(column_params, num_rows): else: arrow_type = None + if not isinstance(arrow_type, pa.lib.Decimal128Type): + vals = pa.array( + column_params.generator, + size=column_params.cardinality, + safe=False, + type=arrow_type, + ) vals = pa.array( - column_params.generator, - size=column_params.cardinality, - safe=False, - type=arrow_type, - ) - # Generate data for current column - return pa.array( - np.random.choice(vals, size=num_rows), + np.random.choice(column_params.generator, size=num_rows) + if isinstance(arrow_type, pa.lib.Decimal128Type) + else np.random.choice(vals, size=num_rows), mask=np.random.choice( [True, False], size=num_rows, @@ -152,9 +154,13 @@ def _generate_column(column_params, num_rows): else None, size=num_rows, safe=False, - type=arrow_type, + type=None + if isinstance(arrow_type, pa.lib.Decimal128Type) + else arrow_type, ) - + if isinstance(arrow_type, pa.lib.Decimal128Type): + vals = vals.cast(arrow_type, safe=False) + return vals else: # Generate data for current column return pa.array( @@ -340,6 +346,22 @@ def rand_dataframe(dtypes_meta, rows, seed=random.randint(0, 2 ** 32 - 1)): dtype=dtype, ) ) + elif dtype == "decimal64": + max_precision = meta.get( + "max_precision", cudf.Decimal64Dtype.MAX_PRECISION + ) + precision = np.random.randint(1, max_precision) + scale = np.random.randint(0, precision) + dtype = cudf.Decimal64Dtype(precision=precision, scale=scale) + column_params.append( + ColumnParameters( + cardinality=cardinality, + null_frequency=null_frequency, + generator=decimal_generator(dtype=dtype, size=cardinality), + is_sorted=False, + dtype=dtype, + ) + ) elif dtype == "category": column_params.append( ColumnParameters( @@ -495,6 +517,18 @@ def boolean_generator(size): return lambda: np.random.choice(a=[False, True], size=size) +def decimal_generator(dtype, size): + max_integral = 10 ** (dtype.precision - dtype.scale) - 1 + max_float = (10 ** dtype.scale - 1) if dtype.scale != 0 else 0 + return lambda: ( + np.random.uniform( + low=-max_integral, + high=max_integral + (max_float / 10 ** dtype.scale), + size=size, + ) + ) + + def get_values_for_nested_data(dtype, lists_max_length): """ Returns list of values based on dtype. From 4715c83cba465ade897eb7c5437a5e02f51125b7 Mon Sep 17 00:00:00 2001 From: Alfred Xu Date: Wed, 5 May 2021 12:00:40 +0800 Subject: [PATCH 34/65] Refactor AggregationJni to support collectSet (#8057) This pull request refactored AggregationJni to support `COLLECT_SET` as a kind of Aggregation (as well as `COLLECT_LIST`). Authors: - Alfred Xu (https://github.com/sperlingxx) Approvers: - Nghia Truong (https://github.com/ttnghia) - Jason Lowe (https://github.com/jlowe) URL: https://github.com/rapidsai/cudf/pull/8057 --- .../main/java/ai/rapids/cudf/Aggregation.java | 146 +++++++++++++++--- java/src/main/native/src/AggregationJni.cpp | 42 +++-- .../test/java/ai/rapids/cudf/TableTest.java | 88 ++++++++++- 3 files changed, 245 insertions(+), 31 deletions(-) diff --git a/java/src/main/java/ai/rapids/cudf/Aggregation.java b/java/src/main/java/ai/rapids/cudf/Aggregation.java index 7d8989571f7..35510372cc1 100644 --- a/java/src/main/java/ai/rapids/cudf/Aggregation.java +++ b/java/src/main/java/ai/rapids/cudf/Aggregation.java @@ -54,11 +54,12 @@ enum Kind { NUNIQUE(15), NTH_ELEMENT(16), ROW_NUMBER(17), - COLLECT(18), - LEAD(19), - LAG(20), - PTX(21), - CUDA(22); + COLLECT_LIST(18), + COLLECT_SET(19), + LEAD(20), + LAG(21), + PTX(22), + CUDA(23); final int nativeId; @@ -77,6 +78,30 @@ public enum NullPolicy { final boolean includeNulls; } + /* + * This is analogous to the native 'null_equality'. + */ + public enum NullEquality { + UNEQUAL(false), + EQUAL(true); + + NullEquality(boolean nullsEqual) { this.nullsEqual = nullsEqual; } + + final boolean nullsEqual; + } + + /* + * This is analogous to the native 'nan_equality'. + */ + public enum NaNEquality { + UNEQUAL(false), + ALL_EQUAL(true); + + NaNEquality(boolean nansEqual) { this.nansEqual = nansEqual; } + + final boolean nansEqual; + } + /** * An Aggregation that only needs a kind and nothing else. */ @@ -280,17 +305,17 @@ long getDefaultOutput() { } } - private static final class CollectAggregation extends Aggregation { + private static final class CollectListAggregation extends Aggregation { private final NullPolicy nullPolicy; - public CollectAggregation(NullPolicy nullPolicy) { - super(Kind.COLLECT); + public CollectListAggregation(NullPolicy nullPolicy) { + super(Kind.COLLECT_LIST); this.nullPolicy = nullPolicy; } @Override long createNativeInstance() { - return Aggregation.createCollectAgg(nullPolicy.includeNulls); + return Aggregation.createCollectListAgg(nullPolicy.includeNulls); } @Override @@ -302,14 +327,55 @@ public int hashCode() { public boolean equals(Object other) { if (this == other) { return true; - } else if (other instanceof CollectAggregation) { - CollectAggregation o = (CollectAggregation) other; + } else if (other instanceof CollectListAggregation) { + CollectListAggregation o = (CollectListAggregation) other; return o.nullPolicy == this.nullPolicy; } return false; } } + private static final class CollectSetAggregation extends Aggregation { + private final NullPolicy nullPolicy; + private final NullEquality nullEquality; + private final NaNEquality nanEquality; + + public CollectSetAggregation(NullPolicy nullPolicy, NullEquality nullEquality, NaNEquality nanEquality) { + super(Kind.COLLECT_SET); + this.nullPolicy = nullPolicy; + this.nullEquality = nullEquality; + this.nanEquality = nanEquality; + } + + @Override + long createNativeInstance() { + return Aggregation.createCollectSetAgg(nullPolicy.includeNulls, + nullEquality.nullsEqual, + nanEquality.nansEqual); + } + + @Override + public int hashCode() { + return 31 * kind.hashCode() + + Boolean.hashCode(nullPolicy.includeNulls) + + Boolean.hashCode(nullEquality.nullsEqual) + + Boolean.hashCode(nanEquality.nansEqual); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof CollectSetAggregation) { + CollectSetAggregation o = (CollectSetAggregation) other; + return o.nullPolicy == this.nullPolicy && + o.nullEquality == this.nullEquality && + o.nanEquality == this.nanEquality; + } + return false; + } + } + protected final Kind kind; protected Aggregation(Kind kind) { @@ -592,19 +658,58 @@ public static Aggregation rowNumber() { } /** - * Collect the values into a list. nulls will be skipped. + * Collect the values into a list. Nulls will be skipped. + * @deprecated please use collectList as instead. */ + @Deprecated public static Aggregation collect() { - return collect(NullPolicy.EXCLUDE); + return collectList(); } /** * Collect the values into a list. - * @param nullPolicy INCLUDE if nulls should be included in the aggregation or EXCLUDE if they - * should be skipped. + * @deprecated please use collectList as instead. + * + * @param nullPolicy Indicates whether to include/exclude nulls during collection. */ + @Deprecated public static Aggregation collect(NullPolicy nullPolicy) { - return new CollectAggregation(nullPolicy); + return collectList(nullPolicy); + } + + /** + * Collect the values into a list. Nulls will be skipped. + */ + public static Aggregation collectList() { + return collectList(NullPolicy.EXCLUDE); + } + + /** + * Collect the values into a list. + * + * @param nullPolicy Indicates whether to include/exclude nulls during collection. + */ + public static Aggregation collectList(NullPolicy nullPolicy) { + return new CollectListAggregation(nullPolicy); + } + + /** + * Collect the values into a set. All null values will be excluded, and all nan values are regarded as + * unique instances. + */ + public static Aggregation collectSet() { + return new CollectSetAggregation(NullPolicy.EXCLUDE, NullEquality.UNEQUAL, NaNEquality.UNEQUAL); + } + + /** + * Collect the values into a set. + * + * @param nullPolicy Indicates whether to include/exclude nulls during collection. + * @param nullEquality Flag to specify whether null entries within each list should be considered equal. + * @param nanEquality Flag to specify whether NaN values in floating point column should be considered equal. + */ + public static Aggregation collectSet(NullPolicy nullPolicy, NullEquality nullEquality, NaNEquality nanEquality) { + return new CollectSetAggregation(nullPolicy, nullEquality, nanEquality); } /** @@ -675,7 +780,12 @@ public static Aggregation lag(int offset, ColumnVector defaultOutput) { private static native long createLeadLagAgg(int kind, int offset); /** - * Create a collect aggregation including nulls or not. + * Create a collect list aggregation including nulls or not. + */ + private static native long createCollectListAgg(boolean includeNulls); + + /** + * Create a collect set aggregation. */ - private static native long createCollectAgg(boolean includeNulls); + private static native long createCollectSetAgg(boolean includeNulls, boolean nullsEqual, boolean nansEqual); } diff --git a/java/src/main/native/src/AggregationJni.cpp b/java/src/main/native/src/AggregationJni.cpp index c5184111edf..63c2c33202e 100644 --- a/java/src/main/native/src/AggregationJni.cpp +++ b/java/src/main/native/src/AggregationJni.cpp @@ -81,11 +81,12 @@ JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Aggregation_createNoParamAgg(JNIEnv case 17: // ROW_NUMBER ret = cudf::make_row_number_aggregation(); break; - // case 18: COLLECT - // case 19: LEAD - // case 20: LAG - // case 21: PTX - // case 22: CUDA + // case 18: COLLECT_LIST + // case 19: COLLECT_SET + // case 20: LEAD + // case 21: LAG + // case 22: PTX + // case 23: CUDA default: throw std::logic_error("Unsupported No Parameter Aggregation Operation"); } @@ -186,10 +187,10 @@ JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Aggregation_createLeadLagAgg(JNIEnv std::unique_ptr ret; // These numbers come from Aggregation.java and must stay in sync switch (kind) { - case 19: // LEAD + case 20: // LEAD ret = cudf::make_lead_aggregation(offset); break; - case 20: // LAG + case 21: // LAG ret = cudf::make_lag_aggregation(offset); break; default: throw std::logic_error("Unsupported Lead/Lag Aggregation Operation"); @@ -199,9 +200,9 @@ JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Aggregation_createLeadLagAgg(JNIEnv CATCH_STD(env, 0); } -JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Aggregation_createCollectAgg(JNIEnv *env, - jclass class_object, - jboolean include_nulls) { +JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Aggregation_createCollectListAgg(JNIEnv *env, + jclass class_object, + jboolean include_nulls) { try { cudf::jni::auto_set_device(env); cudf::null_policy policy = @@ -212,4 +213,25 @@ JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Aggregation_createCollectAgg(JNIEnv CATCH_STD(env, 0); } +JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Aggregation_createCollectSetAgg(JNIEnv *env, + jclass class_object, + jboolean include_nulls, + jboolean nulls_equal, + jboolean nans_equal) { + try { + cudf::jni::auto_set_device(env); + cudf::null_policy null_policy = + include_nulls ? cudf::null_policy::INCLUDE : cudf::null_policy::EXCLUDE; + cudf::null_equality null_equality = + nulls_equal ? cudf::null_equality::EQUAL : cudf::null_equality::UNEQUAL; + cudf::nan_equality nan_equality = + nans_equal ? cudf::nan_equality::ALL_EQUAL : cudf::nan_equality::UNEQUAL; + std::unique_ptr ret = cudf::make_collect_set_aggregation(null_policy, + null_equality, + nan_equality); + return reinterpret_cast(ret.release()); + } + CATCH_STD(env, 0); +} + } // extern "C" diff --git a/java/src/test/java/ai/rapids/cudf/TableTest.java b/java/src/test/java/ai/rapids/cudf/TableTest.java index 670ec585da3..b398157983c 100644 --- a/java/src/test/java/ai/rapids/cudf/TableTest.java +++ b/java/src/test/java/ai/rapids/cudf/TableTest.java @@ -2945,9 +2945,9 @@ void testWindowingRowNumber() { } @Test - void testWindowingCollect() { - Aggregation aggCollectWithNulls = Aggregation.collect(Aggregation.NullPolicy.INCLUDE); - Aggregation aggCollect = Aggregation.collect(); + void testWindowingCollectList() { + Aggregation aggCollectWithNulls = Aggregation.collectList(Aggregation.NullPolicy.INCLUDE); + Aggregation aggCollect = Aggregation.collectList(); WindowOptions winOpts = WindowOptions.builder() .minPeriods(1) .window(2, 1).build(); @@ -4403,6 +4403,88 @@ void testGroupByContiguousSplitGroups() { } } + @Test + void testGroupByCollectListIncludeNulls() { + try (Table input = new Table.TestBuilder() + .column(1, 1, 1, 1, 2, 2, 2, 2, 3, 4) + .column(null, 13, null, 12, 14, null, 15, null, null, 0) + .build(); + Table expected = new Table.TestBuilder() + .column(1, 2, 3, 4) + .column(new ListType(false, new BasicType(true, DType.INT32)), + Arrays.asList(null, 13, null, 12), + Arrays.asList(14, null, 15, null), + Arrays.asList((Integer) null), + Arrays.asList(0)) + .build(); + Table found = input.groupBy(0).aggregate( + Aggregation.collectList(Aggregation.NullPolicy.INCLUDE).onColumn(1))) { + assertTablesAreEqual(expected, found); + } + } + + @Test + void testGroupByCollectSetIncludeNulls() { + // test with null unequal and nan unequal + Aggregation collectSet = Aggregation.collectSet(Aggregation.NullPolicy.INCLUDE, + Aggregation.NullEquality.UNEQUAL, Aggregation.NaNEquality.UNEQUAL); + try (Table input = new Table.TestBuilder() + .column(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4) + .column(null, 13, null, 13, 14, null, 15, null, 4, 1, 1, 4, 0, 0, 0, 0) + .build(); + Table expected = new Table.TestBuilder() + .column(1, 2, 3, 4) + .column(new ListType(false, new BasicType(true, DType.INT32)), + Arrays.asList(13, null, null), Arrays.asList(14, 15, null, null), + Arrays.asList(1, 4), Arrays.asList(0)) + .build(); + Table found = input.groupBy(0).aggregate(collectSet.onColumn(1))) { + assertTablesAreEqual(expected, found); + } + // test with null equal and nan unequal + collectSet = Aggregation.collectSet(Aggregation.NullPolicy.INCLUDE, + Aggregation.NullEquality.EQUAL, Aggregation.NaNEquality.UNEQUAL); + try (Table input = new Table.TestBuilder() + .column(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4) + .column(null, 13.0, null, 13.0, + 14.1, Double.NaN, 13.9, Double.NaN, + Double.NaN, null, 1.0, null, + null, null, null, null) + .build(); + Table expected = new Table.TestBuilder() + .column(1, 2, 3, 4) + .column(new ListType(false, new BasicType(true, DType.FLOAT64)), + Arrays.asList(13.0, null), + Arrays.asList(13.9, 14.1, Double.NaN, Double.NaN), + Arrays.asList(1.0, Double.NaN, null), + Arrays.asList((Integer) null)) + .build(); + Table found = input.groupBy(0).aggregate(collectSet.onColumn(1))) { + assertTablesAreEqual(expected, found); + } + // test with null equal and nan equal + collectSet = Aggregation.collectSet(Aggregation.NullPolicy.INCLUDE, + Aggregation.NullEquality.EQUAL, Aggregation.NaNEquality.ALL_EQUAL); + try (Table input = new Table.TestBuilder() + .column(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4) + .column(null, 13.0, null, 13.0, + 14.1, Double.NaN, 13.9, Double.NaN, + 0.0, 0.0, 0.00, 0.0, + Double.NaN, Double.NaN, null, null) + .build(); + Table expected = new Table.TestBuilder() + .column(1, 2, 3, 4) + .column(new ListType(false, new BasicType(true, DType.FLOAT64)), + Arrays.asList(13.0, null), + Arrays.asList(13.9, 14.1, Double.NaN), + Arrays.asList(0.0), + Arrays.asList(Double.NaN, (Integer) null)) + .build(); + Table found = input.groupBy(0).aggregate(collectSet.onColumn(1))) { + assertTablesAreEqual(expected, found); + } + } + @Test void testRowBitCount() { try (Table t = new Table.TestBuilder() From 9b727dd12bcb12315bfa5c2d56697c393bb678bc Mon Sep 17 00:00:00 2001 From: Jordan Jacobelli Date: Wed, 5 May 2021 12:09:11 +0200 Subject: [PATCH 35/65] ENH Remove 'rapidsai-nightly' conda channel when building main branch (#8152) Remove `rapidsai-nightly` conda channel when building main branch Authors: - Jordan Jacobelli (https://github.com/Ethyling) Approvers: - AJ Schmidt (https://github.com/ajschmidt8) - Ray Douglass (https://github.com/raydouglass) URL: https://github.com/rapidsai/cudf/pull/8152 --- ci/cpu/build.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ci/cpu/build.sh b/ci/cpu/build.sh index 588debc40db..6803d88d8dd 100755 --- a/ci/cpu/build.sh +++ b/ci/cpu/build.sh @@ -42,6 +42,11 @@ gpuci_logger "Activate conda env" . /opt/conda/etc/profile.d/conda.sh conda activate rapids +# Remove rapidsai-nightly channel if we are building main branch +if [ "$SOURCE_BRANCH" = "main" ]; then + conda config --system --remove channels rapidsai-nightly +fi + gpuci_logger "Check compiler versions" python --version $CC --version From 81046ff8f9f571c5d323c1c45819533e07be6135 Mon Sep 17 00:00:00 2001 From: David Wendt <45795991+davidwendt@users.noreply.github.com> Date: Wed, 5 May 2021 07:57:02 -0400 Subject: [PATCH 36/65] Move scalar function definitions from scalar.hpp to scalar.cpp (#8112) The `scalar.hpp` is included in over 70 source files in libcudf and many of its function definitions require it to include other headers. This PR moves all function definitions from the `scalar.hpp` to `scalar.cpp` to help minimize the include dependencies and compile time effected by changes to the functions. This means the templated functions must be defined with all possible types in the .cpp file. This helps reduce the compile parsing and should help simplify the link step since all types are compiled only once. So adding a new type now shows as a compile/link error if it is missing from the `scalar.cpp` definitions. Overall, this improved compile time by about 2-3 minutes or about 10% for my system running `ninja -j16 cudf` with gcc-9.3, nvcc-11.2.142. Authors: - David Wendt (https://github.com/davidwendt) Approvers: - Ram (Ramakrishna Prabhu) (https://github.com/rgsl888prabhu) - Nghia Truong (https://github.com/ttnghia) - Mark Harris (https://github.com/harrism) - Jake Hemstad (https://github.com/jrhemstad) URL: https://github.com/rapidsai/cudf/pull/8112 --- cpp/include/cudf/scalar/scalar.hpp | 185 +++++--------- cpp/src/scalar/scalar.cpp | 390 +++++++++++++++++++++++++++++ cpp/tests/scalar/scalar_test.cpp | 12 + 3 files changed, 465 insertions(+), 122 deletions(-) diff --git a/cpp/include/cudf/scalar/scalar.hpp b/cpp/include/cudf/scalar/scalar.hpp index b1ce1971a5c..3de8762c763 100644 --- a/cpp/include/cudf/scalar/scalar.hpp +++ b/cpp/include/cudf/scalar/scalar.hpp @@ -15,23 +15,14 @@ */ #pragma once -#include +#include #include #include -#include - -#include #include #include #include -#include -#include -#include - -#include - /** * @file * @brief Class definitions for cudf::scalar @@ -62,7 +53,7 @@ class scalar { /** * @brief Returns the scalar's logical value type */ - data_type type() const noexcept { return _type; } + data_type type() const noexcept; /** * @brief Updates the validity of the value @@ -70,10 +61,7 @@ class scalar { * @param is_valid true: set the value to valid. false: set it to null * @param stream CUDA stream used for device memory operations. */ - void set_valid(bool is_valid, rmm::cuda_stream_view stream = rmm::cuda_stream_default) - { - _is_valid.set_value(is_valid, stream); - } + void set_valid(bool is_valid, rmm::cuda_stream_view stream = rmm::cuda_stream_default); /** * @brief Indicates whether the scalar contains a valid value @@ -84,20 +72,17 @@ class scalar { * @return true Value is valid * @return false Value is invalid/null */ - bool is_valid(rmm::cuda_stream_view stream = rmm::cuda_stream_default) const - { - return _is_valid.value(stream); - } + bool is_valid(rmm::cuda_stream_view stream = rmm::cuda_stream_default) const; /** * @brief Returns a raw pointer to the validity bool in device memory */ - bool* validity_data() { return _is_valid.data(); } + bool* validity_data(); /** * @brief Returns a const raw pointer to the validity bool in device memory */ - bool const* validity_data() const { return _is_valid.data(); } + bool const* validity_data() const; protected: data_type _type{type_id::EMPTY}; ///< Logical type of value in the scalar @@ -119,10 +104,7 @@ class scalar { scalar(data_type type, bool is_valid = false, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : _type(type), _is_valid(is_valid, stream, mr) - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); }; namespace detail { @@ -145,41 +127,34 @@ class fixed_width_scalar : public scalar { * @param value New value of scalar * @param stream CUDA stream used for device memory operations. */ - void set_value(T value, rmm::cuda_stream_view stream = rmm::cuda_stream_default) - { - _data.set_value(value, stream); - this->set_valid(true, stream); - } + void set_value(T value, rmm::cuda_stream_view stream = rmm::cuda_stream_default); /** * @brief Implicit conversion operator to get the value of the scalar on the host */ - explicit operator value_type() const { return this->value(rmm::cuda_stream_default); } + explicit operator value_type() const; /** * @brief Get the value of the scalar * * @param stream CUDA stream used for device memory operations. */ - T value(rmm::cuda_stream_view stream = rmm::cuda_stream_default) const - { - return _data.value(stream); - } + T value(rmm::cuda_stream_view stream = rmm::cuda_stream_default) const; /** * @brief Returns a raw pointer to the value in device memory */ - T* data() { return _data.data(); } + T* data(); /** * @brief Returns a const raw pointer to the value in device memory */ - T const* data() const { return _data.data(); } + T const* data() const; protected: rmm::device_scalar _data{}; ///< device memory containing the value - fixed_width_scalar() : scalar(data_type(type_to_id())) {} + fixed_width_scalar(); /** * @brief Construct a new fixed width scalar object @@ -192,10 +167,7 @@ class fixed_width_scalar : public scalar { fixed_width_scalar(T value, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : scalar(data_type(type_to_id()), is_valid, stream, mr), _data(value, stream, mr) - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Construct a new fixed width scalar object from existing device memory. @@ -208,11 +180,7 @@ class fixed_width_scalar : public scalar { fixed_width_scalar(rmm::device_scalar&& data, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : scalar(data_type(type_to_id()), is_valid, stream, mr), - _data{std::forward>(data)} - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); }; } // namespace detail @@ -245,10 +213,7 @@ class numeric_scalar : public detail::fixed_width_scalar { numeric_scalar(T value, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : detail::fixed_width_scalar(value, is_valid, stream, mr) - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Construct a new numeric scalar object from existing device memory. @@ -261,10 +226,7 @@ class numeric_scalar : public detail::fixed_width_scalar { numeric_scalar(rmm::device_scalar&& data, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : detail::fixed_width_scalar(std::forward>(data), is_valid, stream, mr) - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); }; /** @@ -277,9 +239,10 @@ class fixed_point_scalar : public scalar { static_assert(is_fixed_point(), "Unexpected non-fixed_point type."); public: - using rep_type = typename T::rep; + using rep_type = typename T::rep; + using value_type = T; - fixed_point_scalar() : scalar(data_type(type_to_id())){}; + fixed_point_scalar(); ~fixed_point_scalar() = default; fixed_point_scalar(fixed_point_scalar&& other) = default; fixed_point_scalar(fixed_point_scalar const& other) = default; @@ -299,11 +262,7 @@ class fixed_point_scalar : public scalar { numeric::scale_type scale, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : scalar{data_type{type_to_id(), static_cast(scale)}, is_valid, stream, mr}, - _data{value} - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Construct a new fixed_point scalar object from a value and default 0-scale @@ -316,10 +275,7 @@ class fixed_point_scalar : public scalar { fixed_point_scalar(rep_type value, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : scalar{data_type{type_to_id(), 0}, is_valid, stream, mr}, _data{value} - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Construct a new fixed_point scalar object from a fixed_point number @@ -332,10 +288,7 @@ class fixed_point_scalar : public scalar { fixed_point_scalar(T value, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : scalar{data_type{type_to_id(), value.scale()}, is_valid, stream, mr}, _data{value.value()} - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Construct a new fixed_point scalar object from existing device memory. @@ -350,42 +303,31 @@ class fixed_point_scalar : public scalar { numeric::scale_type scale, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : scalar{data_type{type_to_id(), scale}, is_valid, stream, mr}, - _data{std::forward>(data)} - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Get the value of the scalar * * @param stream CUDA stream used for device memory operations. */ - rep_type value(rmm::cuda_stream_view stream = rmm::cuda_stream_default) const - { - return _data.value(stream); - } + rep_type value(rmm::cuda_stream_view stream = rmm::cuda_stream_default) const; /** * @brief Get the decimal32 or decimal64 * * @param stream CUDA stream used for device memory operations. */ - T fixed_point_value(rmm::cuda_stream_view stream = rmm::cuda_stream_default) const - { - using namespace numeric; - return T{scaled_integer{_data.value(stream), scale_type{type().scale()}}}; - } + T fixed_point_value(rmm::cuda_stream_view stream = rmm::cuda_stream_default) const; /** * @brief Returns a raw pointer to the value in device memory */ - rep_type* data() { return _data.data(); } + rep_type* data(); /** * @brief Returns a const raw pointer to the value in device memory */ - rep_type const* data() const { return _data.data(); } + rep_type const* data() const; protected: rmm::device_scalar _data{}; ///< device memory containing the value @@ -398,7 +340,7 @@ class string_scalar : public scalar { public: using value_type = cudf::string_view; - string_scalar() : scalar(data_type(type_id::STRING)) {} + string_scalar(); ~string_scalar() = default; string_scalar(string_scalar&& other) = default; string_scalar(string_scalar const& other) = default; @@ -416,10 +358,7 @@ class string_scalar : public scalar { string_scalar(std::string const& string, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : scalar(data_type(type_id::STRING), is_valid), _data(string.data(), string.size(), stream, mr) - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Construct a new string scalar object from string_view @@ -452,7 +391,7 @@ class string_scalar : public scalar { /** * @brief Implicit conversion operator to get the value of the scalar in a host std::string */ - explicit operator std::string() const { return this->to_string(rmm::cuda_stream_default); } + explicit operator std::string() const; /** * @brief Get the value of the scalar in a host std::string @@ -471,12 +410,12 @@ class string_scalar : public scalar { /** * @brief Returns the size of the string in bytes */ - size_type size() const { return _data.size(); } + size_type size() const; /** * @brief Returns a raw pointer to the string in device memory */ - const char* data() const { return static_cast(_data.data()); } + const char* data() const; protected: rmm::device_buffer _data{}; ///< device memory containing the string @@ -511,10 +450,7 @@ class chrono_scalar : public detail::fixed_width_scalar { chrono_scalar(T value, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : detail::fixed_width_scalar(value, is_valid, stream, mr) - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Construct a new chrono scalar object from existing device memory. @@ -527,16 +463,14 @@ class chrono_scalar : public detail::fixed_width_scalar { chrono_scalar(rmm::device_scalar&& data, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : detail::fixed_width_scalar(std::forward>(data), is_valid, stream, mr) - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); }; template struct timestamp_scalar : chrono_scalar { static_assert(is_timestamp(), "Unexpected non-timestamp type"); using chrono_scalar::chrono_scalar; + using rep_type = typename T::rep; timestamp_scalar() = default; @@ -554,21 +488,19 @@ struct timestamp_scalar : chrono_scalar { timestamp_scalar(Duration2 const& value, bool is_valid, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : chrono_scalar(T{typename T::duration{value}}, is_valid, stream, mr) - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Return the duration in number of ticks since the UNIX epoch. */ - typename T::rep ticks_since_epoch() { return this->value().time_since_epoch().count(); } + rep_type ticks_since_epoch(); }; template struct duration_scalar : chrono_scalar { static_assert(is_duration(), "Unexpected non-duration type"); using chrono_scalar::chrono_scalar; + using rep_type = typename T::rep; duration_scalar() = default; @@ -580,18 +512,15 @@ struct duration_scalar : chrono_scalar { * @param stream CUDA stream used for device memory operations. * @param mr Device memory resource to use for device memory allocation */ - duration_scalar(typename T::rep value, + duration_scalar(rep_type value, bool is_valid, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : chrono_scalar(T{value}, is_valid, stream, mr) - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Return the duration in number of ticks. */ - typename T::rep count() { return this->value().count(); } + rep_type count(); }; /** @@ -599,7 +528,7 @@ struct duration_scalar : chrono_scalar { */ class list_scalar : public scalar { public: - list_scalar() : scalar(data_type(type_id::LIST)) {} + list_scalar(); ~list_scalar() = default; list_scalar(list_scalar&& other) = default; list_scalar(list_scalar const& other) = default; @@ -607,25 +536,37 @@ class list_scalar : public scalar { list_scalar& operator=(list_scalar&& other) = delete; /** - * @brief Construct a new list scalar object from existing device data + * @brief Construct a new list scalar object from column_view + * + * The input column_view is copied. + * + * @param data The column data to copy. + * @param is_valid Whether the value held by the scalar is valid + * @param stream CUDA stream used for device memory operations. + * @param mr Device memory resource to use for device memory allocation + */ + list_scalar(cudf::column_view const& data, + bool is_valid = true, + rmm::cuda_stream_view stream = rmm::cuda_stream_default, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + + /** + * @brief Construct a new list scalar object from existing column. * - * @param elements The elements of the list + * @param data The column to take ownership of * @param is_valid Whether the value held by the scalar is valid * @param stream CUDA stream used for device memory operations. * @param mr Device memory resource to use for device memory allocation */ - list_scalar(cudf::column_view const& elements, + list_scalar(cudf::column&& data, bool is_valid = true, rmm::cuda_stream_view stream = rmm::cuda_stream_default, - rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) - : scalar(data_type(type_id::LIST), is_valid, stream, mr), _data(elements, stream, mr) - { - } + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); /** * @brief Returns a non-owning, immutable view to underlying device data */ - column_view view() const { return _data.view(); } + column_view view() const; private: cudf::column _data; diff --git a/cpp/src/scalar/scalar.cpp b/cpp/src/scalar/scalar.cpp index fe051b1ffc5..858d2c063b3 100644 --- a/cpp/src/scalar/scalar.cpp +++ b/cpp/src/scalar/scalar.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include +#include #include #include @@ -24,6 +26,37 @@ namespace cudf { +scalar::scalar(data_type type, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : _type(type), _is_valid(is_valid, stream, mr) +{ +} + +data_type scalar::type() const noexcept { return _type; } + +void scalar::set_valid(bool is_valid, rmm::cuda_stream_view stream) +{ + _is_valid.set_value(is_valid, stream); +} + +bool scalar::is_valid(rmm::cuda_stream_view stream) const { return _is_valid.value(stream); } + +bool* scalar::validity_data() { return _is_valid.data(); } + +bool const* scalar::validity_data() const { return _is_valid.data(); } + +string_scalar::string_scalar() : scalar(data_type(type_id::STRING)) {} + +string_scalar::string_scalar(std::string const& string, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : scalar(data_type(type_id::STRING), is_valid), _data(string.data(), string.size(), stream, mr) +{ +} + string_scalar::string_scalar(rmm::device_scalar& data, bool is_valid, rmm::cuda_stream_view stream, @@ -46,6 +79,12 @@ string_scalar::value_type string_scalar::value(rmm::cuda_stream_view stream) con return value_type{data(), size()}; } +size_type string_scalar::size() const { return _data.size(); } + +const char* string_scalar::data() const { return static_cast(_data.data()); } + +string_scalar::operator std::string() const { return this->to_string(rmm::cuda_stream_default); } + std::string string_scalar::to_string(rmm::cuda_stream_view stream) const { std::string result; @@ -56,4 +95,355 @@ std::string string_scalar::to_string(rmm::cuda_stream_view stream) const return result; } +template +fixed_point_scalar::fixed_point_scalar() : scalar(data_type(type_to_id())){}; + +template +fixed_point_scalar::fixed_point_scalar(rep_type value, + numeric::scale_type scale, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : scalar{data_type{type_to_id(), static_cast(scale)}, is_valid, stream, mr}, + _data{value} +{ +} + +template +fixed_point_scalar::fixed_point_scalar(rep_type value, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : scalar{data_type{type_to_id(), 0}, is_valid, stream, mr}, _data{value} +{ +} + +template +fixed_point_scalar::fixed_point_scalar(T value, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : scalar{data_type{type_to_id(), value.scale()}, is_valid, stream, mr}, _data{value.value()} +{ +} + +template +fixed_point_scalar::fixed_point_scalar(rmm::device_scalar&& data, + numeric::scale_type scale, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : scalar{data_type{type_to_id(), scale}, is_valid, stream, mr}, + _data{std::forward>(data)} +{ +} + +template +typename fixed_point_scalar::rep_type fixed_point_scalar::value( + rmm::cuda_stream_view stream) const +{ + return _data.value(stream); +} + +template +T fixed_point_scalar::fixed_point_value(rmm::cuda_stream_view stream) const +{ + return value_type{ + numeric::scaled_integer{_data.value(stream), numeric::scale_type{type().scale()}}}; +} + +template +typename fixed_point_scalar::rep_type* fixed_point_scalar::data() +{ + return _data.data(); +} + +template +typename fixed_point_scalar::rep_type const* fixed_point_scalar::data() const +{ + return _data.data(); +} + +/** + * @brief These define the valid fixed-point scalar types. + * + * See `is_fixed_point` in @see cudf/utilities/traits.hpp + * + * Adding a new supported type only requires adding the appropriate line here + * and does not require updating the scalar.hpp file. + */ +template class fixed_point_scalar; +template class fixed_point_scalar; + +namespace detail { + +template +fixed_width_scalar::fixed_width_scalar() : scalar(data_type(type_to_id())) +{ +} + +template +fixed_width_scalar::fixed_width_scalar(T value, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : scalar(data_type(type_to_id()), is_valid, stream, mr), _data(value, stream, mr) +{ +} + +template +fixed_width_scalar::fixed_width_scalar(rmm::device_scalar&& data, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : scalar(data_type(type_to_id()), is_valid, stream, mr), + _data{std::forward>(data)} +{ +} + +template +void fixed_width_scalar::set_value(T value, rmm::cuda_stream_view stream) +{ + _data.set_value(value, stream); + this->set_valid(true, stream); +} + +template +T fixed_width_scalar::value(rmm::cuda_stream_view stream) const +{ + return _data.value(stream); +} + +template +T* fixed_width_scalar::data() +{ + return _data.data(); +} + +template +T const* fixed_width_scalar::data() const +{ + return _data.data(); +} + +template +fixed_width_scalar::operator value_type() const +{ + return this->value(rmm::cuda_stream_default); +} + +/** + * @brief These define the valid fixed-width scalar types. + * + * See `is_fixed_width` in @see cudf/utilities/traits.hpp + * + * Adding a new supported type only requires adding the appropriate line here + * and does not require updating the scalar.hpp file. + */ +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; +template class fixed_width_scalar; + +} // namespace detail + +template +numeric_scalar::numeric_scalar(T value, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : detail::fixed_width_scalar(value, is_valid, stream, mr) +{ +} + +template +numeric_scalar::numeric_scalar(rmm::device_scalar&& data, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : detail::fixed_width_scalar(std::forward>(data), is_valid, stream, mr) +{ +} + +/** + * @brief These define the valid numeric scalar types. + * + * See `is_numeric` in @see cudf/utilities/traits.hpp + * + * Adding a new supported type only requires adding the appropriate line here + * and does not require updating the scalar.hpp file. + */ +template class numeric_scalar; +template class numeric_scalar; +template class numeric_scalar; +template class numeric_scalar; +template class numeric_scalar; +template class numeric_scalar; +template class numeric_scalar; +template class numeric_scalar; +template class numeric_scalar; +template class numeric_scalar; +template class numeric_scalar; + +template +chrono_scalar::chrono_scalar(T value, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : detail::fixed_width_scalar(value, is_valid, stream, mr) +{ +} + +template +chrono_scalar::chrono_scalar(rmm::device_scalar&& data, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : detail::fixed_width_scalar(std::forward>(data), is_valid, stream, mr) +{ +} + +/** + * @brief These define the valid chrono scalar types. + * + * See `is_chrono` in @see cudf/utilities/traits.hpp + * + * Adding a new supported type only requires adding the appropriate line here + * and does not require updating the scalar.hpp file. + */ +template class chrono_scalar; +template class chrono_scalar; +template class chrono_scalar; +template class chrono_scalar; +template class chrono_scalar; +template class chrono_scalar; +template class chrono_scalar; +template class chrono_scalar; +template class chrono_scalar; +template class chrono_scalar; + +template +duration_scalar::duration_scalar(rep_type value, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : chrono_scalar(T{value}, is_valid, stream, mr) +{ +} + +template +typename duration_scalar::rep_type duration_scalar::count() +{ + return this->value().count(); +} + +/** + * @brief These define the valid duration scalar types. + * + * See `is_duration` in @see cudf/utilities/traits.hpp + * + * Adding a new supported type only requires adding the appropriate line here + * and does not require updating the scalar.hpp file. + */ +template class duration_scalar; +template class duration_scalar; +template class duration_scalar; +template class duration_scalar; +template class duration_scalar; + +template +typename timestamp_scalar::rep_type timestamp_scalar::ticks_since_epoch() +{ + return this->value().time_since_epoch().count(); +} + +/** + * @brief These define the valid timestamp scalar types. + * + * See `is_timestamp` in @see cudf/utilities/traits.hpp + * + * Adding a new supported type only requires adding the appropriate line here + * and does not require updating the scalar.hpp file. + */ +template class timestamp_scalar; +template class timestamp_scalar; +template class timestamp_scalar; +template class timestamp_scalar; +template class timestamp_scalar; + +template +template +timestamp_scalar::timestamp_scalar(D const& value, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : chrono_scalar(T{typename T::duration{value}}, is_valid, stream, mr) +{ +} + +#define TS_CTOR(TimestampType, DurationType) \ + template timestamp_scalar::timestamp_scalar( \ + DurationType const&, bool, rmm::cuda_stream_view, rmm::mr::device_memory_resource*); + +/** + * @brief These are the valid combinations of duration types to timestamp types. + */ +TS_CTOR(timestamp_D, duration_D) +TS_CTOR(timestamp_D, int32_t) +TS_CTOR(timestamp_s, duration_D) +TS_CTOR(timestamp_s, duration_s) +TS_CTOR(timestamp_s, int64_t) +TS_CTOR(timestamp_ms, duration_D) +TS_CTOR(timestamp_ms, duration_s) +TS_CTOR(timestamp_ms, duration_ms) +TS_CTOR(timestamp_ms, int64_t) +TS_CTOR(timestamp_us, duration_D) +TS_CTOR(timestamp_us, duration_s) +TS_CTOR(timestamp_us, duration_ms) +TS_CTOR(timestamp_us, duration_us) +TS_CTOR(timestamp_us, int64_t) +TS_CTOR(timestamp_ns, duration_D) +TS_CTOR(timestamp_ns, duration_s) +TS_CTOR(timestamp_ns, duration_ms) +TS_CTOR(timestamp_ns, duration_us) +TS_CTOR(timestamp_ns, duration_ns) +TS_CTOR(timestamp_ns, int64_t) + +list_scalar::list_scalar() : scalar(data_type(type_id::LIST)) {} + +list_scalar::list_scalar(cudf::column_view const& data, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : scalar(data_type(type_id::LIST), is_valid, stream, mr), _data(data, stream, mr) +{ +} + +list_scalar::list_scalar(cudf::column&& data, + bool is_valid, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) + : scalar(data_type(type_id::LIST), is_valid, stream, mr), _data(std::move(data)) +{ +} + +column_view list_scalar::view() const { return _data.view(); } + } // namespace cudf diff --git a/cpp/tests/scalar/scalar_test.cpp b/cpp/tests/scalar/scalar_test.cpp index 2c4427a0fab..b869569bea3 100644 --- a/cpp/tests/scalar/scalar_test.cpp +++ b/cpp/tests/scalar/scalar_test.cpp @@ -167,6 +167,18 @@ TEST_F(ListScalarTest, ConstructNull) EXPECT_FALSE(s.is_valid()); } +TEST_F(ListScalarTest, MoveColumnConstructor) +{ + auto data = cudf::test::fixed_width_column_wrapper{1, 2, 3}; + auto col = cudf::column(data); + auto ptr = col.view().data(); + auto s = cudf::list_scalar(std::move(col)); + + EXPECT_TRUE(s.is_valid()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(data, s.view()); + EXPECT_EQ(ptr, s.view().data()); +} + TEST_F(ListScalarTest, CopyConstructorNonNested) { auto data = cudf::test::fixed_width_column_wrapper{1, 2, 3}; From f54ccd0d7d0ad010e2c56d51a1fb5533257e0e7d Mon Sep 17 00:00:00 2001 From: David Wendt <45795991+davidwendt@users.noreply.github.com> Date: Wed, 5 May 2021 08:23:17 -0400 Subject: [PATCH 37/65] Fix lists strings scatter to handle zero child rows (#8103) Closes #8098 The lists scatter code specialization for strings child was not checking the number of child rows equal to 0. This produces an empty strings column. This PR checks for the case and returns an empty column appropriately. The test-case provided in the #8098 has also been incorporated in the `partition_test.cpp` gtest source. Authors: - David Wendt (https://github.com/davidwendt) Approvers: - Ram (Ramakrishna Prabhu) (https://github.com/rgsl888prabhu) - Mark Harris (https://github.com/harrism) - Nghia Truong (https://github.com/ttnghia) - Robert (Bobby) Evans (https://github.com/revans2) URL: https://github.com/rapidsai/cudf/pull/8103 --- cpp/include/cudf/lists/detail/copying.hpp | 18 ++++++++++++- cpp/include/cudf/lists/detail/scatter.cuh | 11 ++++++++ cpp/src/lists/copying/copying.cu | 14 ++++++++++ cpp/tests/partitioning/partition_test.cpp | 32 ++++++++++++++++++++++- 4 files changed, 73 insertions(+), 2 deletions(-) diff --git a/cpp/include/cudf/lists/detail/copying.hpp b/cpp/include/cudf/lists/detail/copying.hpp index cfa1980e665..548fec7e7f6 100644 --- a/cpp/include/cudf/lists/detail/copying.hpp +++ b/cpp/include/cudf/lists/detail/copying.hpp @@ -39,7 +39,7 @@ namespace detail { * @param start Index to first list to select in the column * @param end One past the index to last list to select in the column * @param stream CUDA stream used for device memory operations and kernel launches - * @param mr Device memory resource used to allocatet the returned column's device memory. + * @param mr Device memory resource used to allocate the returned column's device memory. * @return New lists column of size (end - start) */ std::unique_ptr copy_slice(lists_column_view const& lists, @@ -47,6 +47,22 @@ std::unique_ptr copy_slice(lists_column_view const& lists, size_type end, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr); + +/** + * @brief Create a single-level empty lists column. + * + * An empty lists column contains empty children so the column's + * basic type is recorded. + * + * @param child_type The type used for the child column. + * @param stream CUDA stream used for device memory operations and kernel launches + * @param mr Device memory resource used to allocate the returned column's device memory. + * @return New empty lists column. + */ +std::unique_ptr make_empty_lists_column(data_type child_type, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr); + } // namespace detail } // namespace lists } // namespace cudf diff --git a/cpp/include/cudf/lists/detail/scatter.cuh b/cpp/include/cudf/lists/detail/scatter.cuh index 1de35b95488..5653bc3593c 100644 --- a/cpp/include/cudf/lists/detail/scatter.cuh +++ b/cpp/include/cudf/lists/detail/scatter.cuh @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -431,6 +432,8 @@ struct list_child_constructor { auto const num_child_rows{ cudf::detail::get_value(list_offsets, list_offsets.size() - 1, stream)}; + if (num_child_rows == 0) { return make_empty_column(data_type{type_id::STRING}); } + auto string_views = rmm::device_uvector(num_child_rows, stream); auto populate_string_views = [d_scattered_lists = list_vector.begin(), // unbound_list_view* @@ -521,6 +524,14 @@ struct list_child_constructor { auto const num_child_rows{ cudf::detail::get_value(list_offsets, list_offsets.size() - 1, stream)}; + if (num_child_rows == 0) { + // make an empty lists column using the input child type + return make_empty_lists_column( + source_lists_column_view.child().child(lists_column_view::child_column_index).type(), + stream, + mr); + } + auto child_list_views = rmm::device_uvector(num_child_rows, stream, mr); // Function to convert from parent list_device_view instances to child list_device_views. diff --git a/cpp/src/lists/copying/copying.cu b/cpp/src/lists/copying/copying.cu index c0282f4f1f2..3275a496cfd 100644 --- a/cpp/src/lists/copying/copying.cu +++ b/cpp/src/lists/copying/copying.cu @@ -83,6 +83,20 @@ std::unique_ptr copy_slice(lists_column_view const& lists, cudf::UNKNOWN_NULL_COUNT, std::move(null_mask)); } + +std::unique_ptr make_empty_lists_column(data_type child_type, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr) +{ + return cudf::make_lists_column(0, + make_empty_column(data_type{type_to_id()}), + make_empty_column(child_type), + 0, // Null count + rmm::device_buffer{0, stream, mr}, // Null mask + stream, + mr); +} + } // namespace detail } // namespace lists } // namespace cudf diff --git a/cpp/tests/partitioning/partition_test.cpp b/cpp/tests/partitioning/partition_test.cpp index ed994da20f8..bdd5e7bc780 100644 --- a/cpp/tests/partitioning/partition_test.cpp +++ b/cpp/tests/partitioning/partition_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. + * Copyright (c) 2020-2021, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -280,3 +280,33 @@ TYPED_TEST(PartitionTestFixedPoint, Partition2) run_partition_test(cudf::table_view{{input}}, map, 3, cudf::table_view{{expected}}, offsets); } + +struct PartitionTestNotTyped : public cudf::test::BaseFixture { +}; + +TEST_F(PartitionTestNotTyped, ListOfStringsEmpty) +{ + cudf::test::lists_column_wrapper list{{}, {}}; + auto table_to_partition = cudf::table_view{{list}}; + fixed_width_column_wrapper map{0, 0}; + + auto result = cudf::partition(table_to_partition, map, 2); + CUDF_TEST_EXPECT_TABLES_EQUAL(table_to_partition, result.first->view()); + EXPECT_EQ(3, result.second.size()); +} + +TEST_F(PartitionTestNotTyped, ListOfListOfIntEmpty) +{ + cudf::test::lists_column_wrapper level_2_list; + + fixed_width_column_wrapper level_1_offsets{0, 0, 0}; + std::unique_ptr level_1_list = + cudf::make_lists_column(2, level_1_offsets.release(), level_2_list.release(), 0, {}); + + auto table_to_partition = cudf::table_view{{*level_1_list}}; + fixed_width_column_wrapper map{0, 0}; + + auto result = cudf::partition(table_to_partition, map, 2); + CUDF_TEST_EXPECT_TABLES_EQUAL(table_to_partition, result.first->view()); + EXPECT_EQ(3, result.second.size()); +} From 2ead87c7d0245e5fef08c6fde44c8f7d978874ac Mon Sep 17 00:00:00 2001 From: David Wendt <45795991+davidwendt@users.noreply.github.com> Date: Wed, 5 May 2021 11:29:20 -0400 Subject: [PATCH 38/65] Fix hash of fixed-point type to hash value component (#8141) The following error occurs on a debug build of libcudf. ``` HASHING_TEST HashTestTyped/22.Equality [ RUN ] HashTestTyped/22.Equality ../tests/utilities/column_utilities.cu:235: Failure Failed first difference: lhs[2] = -523478275, rhs[2] = 811206356 Google Test trace: ../tests/hashing/hash_test.cpp:146: <-- line of failure ../tests/utilities/column_utilities.cu:235: Failure Failed first difference: lhs[0] = 757124801, rhs[0] = -1630234276 Google Test trace: ../tests/hashing/hash_test.cpp:152: <-- line of failure ``` This is due to the `cudf::hash()` hashing a fixed-point type by hashing the entire `decimal64` class type. The `decimal64` class contains an `int64_t` value type and an `int32_t` scale type which is a total of 12 bytes. The `sizeof(decimal64)` is 16 bytes and the hash in the case hashes the entire struct -- including the random 4 bytes of padding. Snippet of one of the hash algorithms: https://github.com/rapidsai/cudf/blob/7623f3978b17be3782c508441fafc45d8d9e258e/cpp/include/cudf/detail/utilities/hash_functions.cuh#L436-L440 This PR changes the hash for fixed-point types by specializing for `decimal32` and `decimal64` to hash only the value component instead of the entire class object. This specialization already exists in the `SparkMurmurHash3_32` implementation. The PR also modifies the `row_hasher` to use `type_dispatch` to help reduce the code size as well. Authors: - David Wendt (https://github.com/davidwendt) Approvers: - Jake Hemstad (https://github.com/jrhemstad) - MithunR (https://github.com/mythrocks) - Nghia Truong (https://github.com/ttnghia) URL: https://github.com/rapidsai/cudf/pull/8141 --- .../cudf/detail/utilities/hash_functions.cuh | 14 ++++++++++ cpp/include/cudf/table/row_operators.cuh | 27 ++++++++++--------- cpp/src/hash/hashing.cu | 11 ++++---- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/cpp/include/cudf/detail/utilities/hash_functions.cuh b/cpp/include/cudf/detail/utilities/hash_functions.cuh index 7f3c05134e2..888a892d003 100644 --- a/cpp/include/cudf/detail/utilities/hash_functions.cuh +++ b/cpp/include/cudf/detail/utilities/hash_functions.cuh @@ -549,6 +549,20 @@ hash_value_type CUDA_DEVICE_CALLABLE MurmurHash3_32::operator()(double c return this->compute_floating_point(key); } +template <> +hash_value_type CUDA_DEVICE_CALLABLE +MurmurHash3_32::operator()(numeric::decimal32 const& key) const +{ + return this->compute(key.value()); +} + +template <> +hash_value_type CUDA_DEVICE_CALLABLE +MurmurHash3_32::operator()(numeric::decimal64 const& key) const +{ + return this->compute(key.value()); +} + template <> hash_value_type CUDA_DEVICE_CALLABLE MurmurHash3_32::operator()(cudf::list_view const& key) const diff --git a/cpp/include/cudf/table/row_operators.cuh b/cpp/include/cudf/table/row_operators.cuh index 61d714c5538..bec5299ab77 100644 --- a/cpp/include/cudf/table/row_operators.cuh +++ b/cpp/include/cudf/table/row_operators.cuh @@ -475,17 +475,19 @@ class row_hasher { // Hash the first column w/ the seed auto const initial_hash = hash_combiner(hash_value_type{0}, - type_dispatcher(_table.column(0).type(), - element_hasher_with_seed{_seed}, - _table.column(0), - row_index)); + type_dispatcher( + _table.column(0).type(), + element_hasher_with_seed{_seed}, + _table.column(0), + row_index)); // Hashes an element in a column auto hasher = [=](size_type column_index) { - return cudf::type_dispatcher(_table.column(column_index).type(), - element_hasher{}, - _table.column(column_index), - row_index); + return cudf::type_dispatcher( + _table.column(column_index).type(), + element_hasher{}, + _table.column(column_index), + row_index); }; // Hash each element and combine all the hash values together @@ -528,10 +530,11 @@ class row_hasher_initial_values { // Hashes an element in a column and combines with an initial value auto hasher = [=](size_type column_index) { - auto hash_value = cudf::type_dispatcher(_table.column(column_index).type(), - element_hasher{}, - _table.column(column_index), - row_index); + auto hash_value = + cudf::type_dispatcher(_table.column(column_index).type(), + element_hasher{}, + _table.column(column_index), + row_index); return hash_combiner(_initial_hash[column_index], hash_value); }; diff --git a/cpp/src/hash/hashing.cu b/cpp/src/hash/hashing.cu index 2cfcd1dbcc8..2bcbedd8ef3 100644 --- a/cpp/src/hash/hashing.cu +++ b/cpp/src/hash/hashing.cu @@ -122,11 +122,12 @@ std::unique_ptr md5_hash(table_view const& input, MD5Hash hasher = MD5Hash{}; for (int col_index = 0; col_index < device_input.num_columns(); col_index++) { if (device_input.column(col_index).is_valid(row_index)) { - cudf::type_dispatcher(device_input.column(col_index).type(), - hasher, - device_input.column(col_index), - row_index, - &hash_state); + cudf::type_dispatcher( + device_input.column(col_index).type(), + hasher, + device_input.column(col_index), + row_index, + &hash_state); } } hasher.finalize(&hash_state, d_chars + (row_index * 32)); From c0f8176654f81a8358c8f59e2a0d284428a79d26 Mon Sep 17 00:00:00 2001 From: Paul Taylor Date: Wed, 5 May 2021 14:18:48 -0500 Subject: [PATCH 39/65] Allow users to set jitify cache file limit via an environment variable (#8132) Jitify limits the file count of the kernel cache, defaulting to the number of JIT kernels to keep in memory. Limiting to 100 files on disk leads to thrashing if running workloads that exercise all the binops (like unit tests). This PR adds a `LIBCUDF_KERNEL_CACHE_FILE_LIMIT` environment variable so users can control the number of files Jitify should keep on disk. Authors: - Paul Taylor (https://github.com/trxcllnt) - Christopher Harris (https://github.com/cwharris) Approvers: - Devavret Makkar (https://github.com/devavret) - David Wendt (https://github.com/davidwendt) - Christopher Harris (https://github.com/cwharris) URL: https://github.com/rapidsai/cudf/pull/8132 --- cpp/src/jit/cache.cpp | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/cpp/src/jit/cache.cpp b/cpp/src/jit/cache.cpp index cb401c184ee..f86018276e7 100644 --- a/cpp/src/jit/cache.cpp +++ b/cpp/src/jit/cache.cpp @@ -20,6 +20,8 @@ #include #include +#include + namespace cudf { namespace jit { @@ -102,6 +104,15 @@ std::string get_program_cache_dir() #endif } +void try_parse_numeric_env_var(std::size_t& result, char const* const env_name) +{ + auto value = std::getenv(env_name); + + if (value != nullptr) { + result = std::stoull(value); // fails if env var contains invalid value. + } +} + jitify2::ProgramCache<>& get_program_cache(jitify2::PreprocessedProgramData preprog) { static std::mutex caches_mutex{}; @@ -112,9 +123,26 @@ jitify2::ProgramCache<>& get_program_cache(jitify2::PreprocessedProgramData prep auto existing_cache = caches.find(preprog.name()); if (existing_cache == caches.end()) { - auto res = caches.insert( - {preprog.name(), - std::make_unique>(100, preprog, nullptr, get_program_cache_dir())}); + std::size_t kernel_limit_proc = std::numeric_limits::max(); + std::size_t kernel_limit_disk = std::numeric_limits::max(); + try_parse_numeric_env_var(kernel_limit_proc, "LIBCUDF_KERNEL_CACHE_LIMIT_PER_PROCESS"); + try_parse_numeric_env_var(kernel_limit_disk, "LIBCUDF_KERNEL_CACHE_LIMIT_DISK"); + + auto cache_dir = get_program_cache_dir(); + + if (kernel_limit_disk == 0) { + // if kernel_limit_disk is zero, jitify will assign it the value of kernel_limit_proc. + // to avoid this, we treat zero as "disable disk caching" by not providing the cache dir. + cache_dir = {}; + } + + auto res = caches.insert({preprog.name(), + std::make_unique>( // + kernel_limit_proc, + preprog, + nullptr, + cache_dir, + kernel_limit_disk)}); existing_cache = res.first; } From 8cba3b05b96f8dc565bf973bca22933d5780238b Mon Sep 17 00:00:00 2001 From: Ashwin Srinath <3190405+shwina@users.noreply.github.com> Date: Wed, 5 May 2021 15:23:44 -0400 Subject: [PATCH 40/65] Enable join results with size > INT32_MAX (#8139) Closes https://github.com/rapidsai/cudf/issues/8121 Authors: - Ashwin Srinath (https://github.com/shwina) Approvers: - Robert Maynard (https://github.com/robertmaynard) - Jake Hemstad (https://github.com/jrhemstad) - Nghia Truong (https://github.com/ttnghia) - Robert (Bobby) Evans (https://github.com/revans2) URL: https://github.com/rapidsai/cudf/pull/8139 --- cpp/src/join/hash_join.cu | 7 ++++--- cpp/src/join/hash_join.cuh | 26 ++++++++++---------------- cpp/src/join/join_kernels.cuh | 14 ++++++-------- cpp/tests/join/join_tests.cpp | 23 ----------------------- 4 files changed, 20 insertions(+), 50 deletions(-) diff --git a/cpp/src/join/hash_join.cu b/cpp/src/join/hash_join.cu index d3a55c0564b..2624ea68629 100644 --- a/cpp/src/join/hash_join.cu +++ b/cpp/src/join/hash_join.cu @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -176,7 +177,7 @@ std::unique_ptr> build_join_ CUDF_EXPECTS(0 != build_device_table->num_rows(), "Build side table has no rows"); size_type const build_table_num_rows{build_device_table->num_rows()}; - size_t const hash_table_size = compute_hash_table_size(build_table_num_rows); + std::size_t const hash_table_size = compute_hash_table_size(build_table_num_rows); auto hash_table = multimap_type::create(hash_table_size, stream, @@ -228,7 +229,7 @@ probe_join_hash_table(cudf::table_device_view build_table, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource *mr) { - size_type estimated_size = estimate_join_output_size( + std::size_t estimated_size = estimate_join_output_size( build_table, probe_table, hash_table, compare_nulls, stream); // If the estimated output size is zero, return immediately @@ -242,7 +243,7 @@ probe_join_hash_table(cudf::table_device_view build_table, // As such we will need to de-allocate memory and re-allocate memory to ensure // that the final output is correct. rmm::device_scalar write_index(0, stream); - size_type join_size{0}; + std::size_t join_size{0}; auto left_indices = std::make_unique>(0, stream, mr); auto right_indices = std::make_unique>(0, stream, mr); diff --git a/cpp/src/join/hash_join.cuh b/cpp/src/join/hash_join.cuh index 175895250fe..e6df2b58b15 100644 --- a/cpp/src/join/hash_join.cuh +++ b/cpp/src/join/hash_join.cuh @@ -33,6 +33,7 @@ #include +#include #include namespace cudf { @@ -62,14 +63,12 @@ namespace detail { * @return An estimate of the size of the output of the join operation */ template -size_type estimate_join_output_size(table_device_view build_table, - table_device_view probe_table, - multimap_type const& hash_table, - null_equality compare_nulls, - rmm::cuda_stream_view stream) +std::size_t estimate_join_output_size(table_device_view build_table, + table_device_view probe_table, + multimap_type const& hash_table, + null_equality compare_nulls, + rmm::cuda_stream_view stream) { - using estimate_size_type = int64_t; // use 64-bit size so we can detect overflow - const size_type build_table_num_rows{build_table.num_rows()}; const size_type probe_table_num_rows{probe_table.num_rows()}; @@ -100,8 +99,8 @@ size_type estimate_join_output_size(table_device_view build_table, if (probe_to_build_ratio > MAX_RATIO) { sample_probe_num_rows = build_table_num_rows; } // Allocate storage for the counter used to get the size of the join output - estimate_size_type h_size_estimate{0}; - rmm::device_scalar size_estimate(0, stream); + std::size_t h_size_estimate{0}; + rmm::device_scalar size_estimate(0, stream); CHECK_CUDA(stream.value()); @@ -148,11 +147,6 @@ size_type estimate_join_output_size(table_device_view build_table, h_size_estimate = size_estimate.value(stream); } - // Detect overflow - CUDF_EXPECTS(h_size_estimate < - static_cast(std::numeric_limits::max()), - "Maximum join output size exceeded"); - // If the size estimate is non-zero, then we have a valid estimate and can break // If sample_probe_num_rows >= probe_table_num_rows, then we've sampled the entire // probe table, in which case the estimate is exact and we can break @@ -165,12 +159,12 @@ size_type estimate_join_output_size(table_device_view build_table, constexpr size_type GROW_RATIO{2}; sample_probe_num_rows *= GROW_RATIO; probe_to_build_ratio = - static_cast(std::ceil(static_cast(probe_to_build_ratio) / GROW_RATIO)); + static_cast(std::ceil(static_cast(probe_to_build_ratio) / GROW_RATIO)); } } while (true); - return static_cast(h_size_estimate); + return h_size_estimate; } /** diff --git a/cpp/src/join/join_kernels.cuh b/cpp/src/join/join_kernels.cuh index c353ec2e895..4298706987c 100644 --- a/cpp/src/join/join_kernels.cuh +++ b/cpp/src/join/join_kernels.cuh @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include @@ -119,17 +120,14 @@ __global__ void build_hash_table(multimap_type multi_map, * @param[in] probe_table_num_rows The number of rows in the probe table * @param[out] output_size The resulting output size */ -template +template __global__ void compute_join_output_size(multimap_type multi_map, table_device_view build_table, table_device_view probe_table, row_hash hash_probe, row_equality check_row_equality, const cudf::size_type probe_table_num_rows, - estimate_size_type* output_size) + std::size_t* output_size) { // This kernel probes multiple elements in the probe_table and store the number of matches found // inside a register. A block reduction is used at the end to calculate the matches per thread @@ -193,9 +191,9 @@ __global__ void compute_join_output_size(multimap_type multi_map, } } - using BlockReduce = cub::BlockReduce; + using BlockReduce = cub::BlockReduce; __shared__ typename BlockReduce::TempStorage temp_storage; - estimate_size_type block_counter = BlockReduce(temp_storage).Sum(thread_counter); + std::size_t block_counter = BlockReduce(temp_storage).Sum(thread_counter); // Add block counter to global counter if (threadIdx.x == 0) atomicAdd(output_size, block_counter); @@ -311,7 +309,7 @@ __global__ void probe_hash_table(multimap_type multi_map, size_type* join_output_l, size_type* join_output_r, cudf::size_type* current_idx, - const cudf::size_type max_size) + const std::size_t max_size) { constexpr int num_warps = block_size / detail::warp_size; __shared__ size_type current_idx_shared[num_warps]; diff --git a/cpp/tests/join/join_tests.cpp b/cpp/tests/join/join_tests.cpp index 9593510d5de..b0a2149d50f 100644 --- a/cpp/tests/join/join_tests.cpp +++ b/cpp/tests/join/join_tests.cpp @@ -607,29 +607,6 @@ TEST_F(JoinTest, LeftJoinOnNulls) CUDF_TEST_EXPECT_TABLES_EQUIVALENT(*sorted_gold, *sorted_result); } -TEST_F(JoinTest, InnerJoinSizeOverflow) -{ - auto zero = cudf::make_numeric_scalar(cudf::data_type(cudf::type_id::INT32)); - zero->set_valid(true); - static_cast *>(zero.get())->set_value(0); - - // Should cause size overflow, raise exception - int32_t left = 4; - int32_t right = 1073741825; - - auto col0_0 = cudf::make_column_from_scalar(*zero, left); - auto col1_0 = cudf::make_column_from_scalar(*zero, right); - - CVector cols0, cols1; - cols0.push_back(std::move(col0_0)); - cols1.push_back(std::move(col1_0)); - - Table t0(std::move(cols0)); - Table t1(std::move(cols1)); - - EXPECT_THROW(cudf::inner_join(t0, t1, {0}, {0}), cudf::logic_error); -} - TEST_F(JoinTest, InnerJoinNoNulls) { column_wrapper col0_0{{3, 1, 2, 0, 2}}; From 559e8a3ba5f81c7c8960edf4b0c684b17e03f3a4 Mon Sep 17 00:00:00 2001 From: Raza Jafri Date: Wed, 5 May 2021 13:49:52 -0700 Subject: [PATCH 41/65] Java bindings for Parquet struct support (#7998) This PR adds support for writing Structs to Parquet. Authors: - Raza Jafri (https://github.com/razajafri) Approvers: - Vukasin Milovanovic (https://github.com/vuule) - Mark Harris (https://github.com/harrism) - Robert (Bobby) Evans (https://github.com/revans2) - Jason Lowe (https://github.com/jlowe) URL: https://github.com/rapidsai/cudf/pull/7998 --- cpp/include/cudf/io/parquet.hpp | 11 + .../cudf/ParquetColumnWriterOptions.java | 448 ++++++++++++++++++ .../ai/rapids/cudf/ParquetWriterOptions.java | 146 ++++-- java/src/main/java/ai/rapids/cudf/Table.java | 52 +- java/src/main/native/CMakeLists.txt | 4 +- java/src/main/native/src/TableJni.cpp | 149 +++--- .../test/java/ai/rapids/cudf/TableTest.java | 121 +++-- 7 files changed, 757 insertions(+), 174 deletions(-) create mode 100644 java/src/main/java/ai/rapids/cudf/ParquetColumnWriterOptions.java diff --git a/cpp/include/cudf/io/parquet.hpp b/cpp/include/cudf/io/parquet.hpp index 7cb3db1eb30..178e46a0c5c 100644 --- a/cpp/include/cudf/io/parquet.hpp +++ b/cpp/include/cudf/io/parquet.hpp @@ -392,6 +392,17 @@ class column_in_metadata { std::vector children; public: + /** + * @brief Get the children of this column metadata + * + * @return this for chaining + */ + column_in_metadata& add_child(column_in_metadata const& child) + { + children.push_back(child); + return *this; + } + /** * @brief Set the name of this column * diff --git a/java/src/main/java/ai/rapids/cudf/ParquetColumnWriterOptions.java b/java/src/main/java/ai/rapids/cudf/ParquetColumnWriterOptions.java new file mode 100644 index 00000000000..f5b0a0f74b3 --- /dev/null +++ b/java/src/main/java/ai/rapids/cudf/ParquetColumnWriterOptions.java @@ -0,0 +1,448 @@ +/* + * + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package ai.rapids.cudf; + +import java.util.ArrayList; +import java.util.List; + +/** + * Per column settings for writing Parquet files. + */ +public class ParquetColumnWriterOptions { + private boolean isTimestampTypeInt96; + private int precision; + private boolean isNullable; + private String columName; + private ParquetColumnWriterOptions(AbstractStructBuilder builder) { + this.columName = builder.name; + this.isNullable = builder.isNullable; + this.childColumnOptions = + (ParquetColumnWriterOptions[]) builder.children.toArray(new ParquetColumnWriterOptions[0]); + } + + /** + * Constructor used for list + */ + private ParquetColumnWriterOptions(ListBuilder builder) { + assert(builder.children.size() == 1) : "Lists can only have one child"; + this.columName = builder.name; + this.isNullable = builder.isNullable; + // we are adding the child twice even though lists have one child only because the way the cudf + // has implemented this it requires two children to be set for the list, but it drops the + // first one. This is something that is a lower priority and might be fixed in future + this.childColumnOptions = + new ParquetColumnWriterOptions[]{DUMMY_CHILD, builder.children.get(0)}; + } + + protected ParquetColumnWriterOptions[] childColumnOptions = {}; + protected abstract static class AbstractStructBuilder extends NestedBuilder { + /** + * Builder specific to build a Struct meta + */ + public AbstractStructBuilder(String name, boolean isNullable) { + super(name, isNullable); + } + + protected AbstractStructBuilder() { + super(); + } + } + + // This child is needed as the first child of a List column meta due to how cudf has been + // implemented. Cudf drops the first child from the meta if a column is a LIST. This is done + // this way due to some complications in the parquet reader. There was change to fix this here: + // https://github.com/rapidsai/cudf/pull/7461/commits/5ce33b40abb87cc7b76b5efeb0a3a0215f9ef6fb + // but it was reverted later on here: + // https://github.com/rapidsai/cudf/pull/7461/commits/f248eb7265de995a95f998d46d897fb0ae47f53e + static ParquetColumnWriterOptions DUMMY_CHILD = new ParquetColumnWriterOptions("DUMMY"); + + public static abstract class NestedBuilder { + protected List children = new ArrayList<>(); + protected boolean isNullable = true; + protected String name = ""; + + /** + * Builder specific to build a Struct meta + */ + protected NestedBuilder(String name, boolean isNullable) { + this.name = name; + this.isNullable = isNullable; + } + + protected NestedBuilder() {} + + protected ParquetColumnWriterOptions withColumns(String name, boolean isNullable) { + return new ParquetColumnWriterOptions(name, isNullable); + } + + protected ParquetColumnWriterOptions withDecimal(String name, int precision, + boolean isNullable) { + return new ParquetColumnWriterOptions(name, false, precision, isNullable); + } + + protected ParquetColumnWriterOptions withTimestamp(String name, boolean isInt96, + boolean isNullable) { + return new ParquetColumnWriterOptions(name, isInt96, 0, isNullable); + } + + /** + * Set the list column meta. + * Lists should have only one child in ColumnVector, but the metadata expects a + * LIST column to have two children and the first child to be the + * {@link ParquetColumnWriterOptions#DUMMY_CHILD}. + * This is the current behavior in cudf and will change in future + * @return this for chaining. + */ + public T withListColumn(ParquetListColumnWriterOptions child) { + assert (child.getChildColumnOptions().length == 2) : "Lists can only have two children"; + if (child.getChildColumnOptions()[0] != DUMMY_CHILD) { + throw new IllegalArgumentException("First child in the list has to be DUMMY_CHILD"); + } + if (child.getChildColumnOptions()[1].getColumName().isEmpty()) { + throw new IllegalArgumentException("Column name can't be empty"); + } + children.add(child); + return (T) this; + } + + /** + * Set a child struct meta data + * @return this for chaining. + */ + public T withStructColumn(ParquetStructColumnWriterOptions child) { + for (ParquetColumnWriterOptions opt: child.getChildColumnOptions()) { + if (opt.getColumName().isEmpty()) { + throw new IllegalArgumentException("Column name can't be empty"); + } + } + children.add(child); + return (T) this; + } + + /** + * Set column name + */ + public T withNonNullableColumns(String... name) { + withColumns(false, name); + return (T) this; + } + + /** + * Set nullable column meta data + */ + public T withNullableColumns(String... name) { + withColumns(true, name); + return (T) this; + } + + /** + * Set a simple child meta data + * @return this for chaining. + */ + public T withColumns(boolean nullable, String... name) { + for (String n : name) { + children.add(withColumns(n, nullable)); + } + return (T) this; + } + + /** + * Set a Decimal child meta data + * @return this for chaining. + */ + public T withDecimalColumn(String name, int precision, boolean nullable) { + children.add(withDecimal(name, precision, nullable)); + return (T) this; + } + + /** + * Set a Decimal child meta data + * @return this for chaining. + */ + public T withNullableDecimalColumn(String name, int precision) { + withDecimalColumn(name, precision, true); + return (T) this; + } + + /** + * Set a Decimal child meta data + * @return this for chaining. + */ + public T withDecimalColumn(String name, int precision) { + withDecimalColumn(name, precision, false); + return (T) this; + } + + /** + * Set a timestamp child meta data + * @return this for chaining. + */ + public T withTimestampColumn(String name, boolean isInt96, boolean nullable) { + children.add(withTimestamp(name, isInt96, nullable)); + return (T) this; + } + + /** + * Set a timestamp child meta data + * @return this for chaining. + */ + public T withTimestampColumn(String name, boolean isInt96) { + withTimestampColumn(name, isInt96, false); + return (T) this; + } + + /** + * Set a timestamp child meta data + * @return this for chaining. + */ + public T withNullableTimestampColumn(String name, boolean isInt96) { + withTimestampColumn(name, isInt96, true); + return (T) this; + } + + public abstract V build(); + } + + ParquetColumnWriterOptions(String columnName, boolean isTimestampTypeInt96, + int precision, boolean isNullable) { + this.isTimestampTypeInt96 = isTimestampTypeInt96; + this.precision = precision; + this.isNullable = isNullable; + this.columName = columnName; + } + + ParquetColumnWriterOptions(String columnName, boolean isNullable) { + this.isTimestampTypeInt96 = false; + this.precision = 0; + this.isNullable = isNullable; + this.columName = columnName; + } + + ParquetColumnWriterOptions(String columnName) { + this(columnName, true); + } + + @FunctionalInterface + protected interface ByteArrayProducer { + boolean[] apply(ParquetColumnWriterOptions opt); + } + + @FunctionalInterface + protected interface IntArrayProducer { + int[] apply(ParquetColumnWriterOptions opt); + } + + boolean[] getFlatIsTimeTypeInt96() { + boolean[] ret = {isTimestampTypeInt96}; + if (childColumnOptions.length > 0) { + return getFlatBooleans(ret, (opt) -> opt.getFlatIsTimeTypeInt96()); + } else { + return ret; + } + } + + protected boolean[] getFlatBooleans(boolean[] ret, ByteArrayProducer producer) { + boolean[][] childResults = new boolean[childColumnOptions.length][]; + int totalChildrenFlatLength = ret.length; + for (int i = 0 ; i < childColumnOptions.length ; i++) { + ParquetColumnWriterOptions opt = childColumnOptions[i]; + childResults[i] = producer.apply(opt); + totalChildrenFlatLength += childResults[i].length; + } + + boolean[] result = new boolean[totalChildrenFlatLength]; + System.arraycopy(ret, 0, result, 0, ret.length); + int copiedSoFar = ret.length; + for (int i = 0 ; i < childColumnOptions.length ; i++) { + System.arraycopy(childResults[i], 0, result, copiedSoFar, childResults[i].length); + copiedSoFar += childResults[i].length; + } + return result; + } + + int[] getFlatPrecision() { + int[] ret = {precision}; + if (childColumnOptions.length > 0) { + return getFlatInts(ret, (opt) -> opt.getFlatPrecision()); + } else { + return ret; + } + } + + boolean[] getFlatIsNullable() { + boolean[] ret = {isNullable}; + if (childColumnOptions.length > 0) { + return getFlatBooleans(ret, (opt) -> opt.getFlatIsNullable()); + } else { + return ret; + } + } + + int[] getFlatNumChildren() { + int[] ret = {childColumnOptions.length}; + if (childColumnOptions.length > 0) { + return getFlatInts(ret, (opt) -> opt.getFlatNumChildren()); + } else { + return ret; + } + } + + protected int[] getFlatInts(int[] ret, IntArrayProducer producer) { + int[][] childResults = new int[childColumnOptions.length][]; + int totalChildrenFlatLength = ret.length; + for (int i = 0 ; i < childColumnOptions.length ; i++) { + ParquetColumnWriterOptions opt = childColumnOptions[i]; + childResults[i] = producer.apply(opt); + totalChildrenFlatLength += childResults[i].length; + } + + int[] result = new int[totalChildrenFlatLength]; + System.arraycopy(ret, 0, result, 0, ret.length); + int copiedSoFar = ret.length; + for (int i = 0 ; i < childColumnOptions.length ; i++) { + System.arraycopy(childResults[i], 0, result, copiedSoFar, childResults[i].length); + copiedSoFar += childResults[i].length; + } + return result; + } + + String[] getFlatColumnNames() { + String[] ret = {columName}; + if (childColumnOptions.length > 0) { + return getFlatColumnNames(ret); + } else { + return ret; + } + } + + protected String[] getFlatColumnNames(String[] ret) { + String[][] childResults = new String[childColumnOptions.length][]; + int totalChildrenFlatLength = ret.length; + for (int i = 0 ; i < childColumnOptions.length ; i++) { + ParquetColumnWriterOptions opt = childColumnOptions[i]; + childResults[i] = opt.getFlatColumnNames(); + totalChildrenFlatLength += childResults[i].length; + } + + String[] result = new String[totalChildrenFlatLength]; + System.arraycopy(ret, 0, result, 0, ret.length); + int copiedSoFar = ret.length; + for (int i = 0 ; i < childColumnOptions.length ; i++) { + System.arraycopy(childResults[i], 0, result, copiedSoFar, childResults[i].length); + copiedSoFar += childResults[i].length; + } + return result; + } + + /** + * Creates a ListBuilder for column called 'name' + */ + public static ListBuilder listBuilder(String name) { + return new ListBuilder(name, true); + } + + /** + * Creates a ListBuilder for column called 'name' + */ + public static ListBuilder listBuilder(String name, boolean isNullable) { + return new ListBuilder(name, isNullable); + } + + /** + * Creates a StructBuilder for column called 'name' + */ + public static StructBuilder structBuilder(String name, boolean isNullable) { + return new StructBuilder(name, isNullable); + } + + /** + * Creates a StructBuilder for column called 'name' + */ + public static StructBuilder structBuilder(String name) { + return new StructBuilder(name, true); + } + + /** + * Return if the column can have null values + */ + public String getColumName() { + return columName; + } + + /** + * Return if the column can have null values + */ + public boolean isNullable() { + return isNullable; + } + + /** + * Return the precision for this column + */ + public int getPrecision() { + return precision; + } + + /** + * Returns true if the writer is expected to write timestamps in INT96 + */ + public boolean isTimestampTypeInt96() { + return isTimestampTypeInt96; + } + + /** + * Return the child columnOptions for this column + */ + public ParquetColumnWriterOptions[] getChildColumnOptions() { + return childColumnOptions; + } + + public static class ParquetStructColumnWriterOptions extends ParquetColumnWriterOptions { + protected ParquetStructColumnWriterOptions(AbstractStructBuilder builder) { + super(builder); + } + } + + public static class ParquetListColumnWriterOptions extends ParquetColumnWriterOptions { + protected ParquetListColumnWriterOptions(ListBuilder builder) { + super(builder); + } + } + + public static class StructBuilder extends AbstractStructBuilder { + public StructBuilder(String name, boolean isNullable) { + super(name, isNullable); + } + + public ParquetStructColumnWriterOptions build() { + return new ParquetStructColumnWriterOptions(this); + } + } + + public static class ListBuilder extends NestedBuilder { + public ListBuilder(String name, boolean isNullable) { + super(name, isNullable); + } + + public ParquetListColumnWriterOptions build() { + return new ParquetListColumnWriterOptions(this); + } + } +} diff --git a/java/src/main/java/ai/rapids/cudf/ParquetWriterOptions.java b/java/src/main/java/ai/rapids/cudf/ParquetWriterOptions.java index 2e793494b7b..9992ae9eaf1 100644 --- a/java/src/main/java/ai/rapids/cudf/ParquetWriterOptions.java +++ b/java/src/main/java/ai/rapids/cudf/ParquetWriterOptions.java @@ -18,10 +18,58 @@ package ai.rapids.cudf; +import java.util.LinkedHashMap; +import java.util.Map; + /** - * Settings for writing Parquet files. + * This class represents settings for writing Parquet files. It includes meta data information + * that will be used by the Parquet writer to write the file */ -public class ParquetWriterOptions extends CompressedMetadataWriterOptions { +public final class ParquetWriterOptions extends ParquetColumnWriterOptions.ParquetStructColumnWriterOptions { + private final CompressionType compressionType; + private final Map metadata; + private final StatisticsFrequency statsGranularity; + + private ParquetWriterOptions(Builder builder) { + super(builder); + this.statsGranularity = builder.statsGranularity; + this.compressionType = builder.compressionType; + this.metadata = builder.metadata; + } + + @Override + boolean[] getFlatIsTimeTypeInt96() { + return super.getFlatBooleans(new boolean[]{}, (opt) -> opt.getFlatIsTimeTypeInt96()); + } + + @Override + int[] getFlatPrecision() { + return super.getFlatInts(new int[]{}, (opt) -> opt.getFlatPrecision()); + } + + @Override + int[] getFlatNumChildren() { + return super.getFlatInts(new int[]{}, (opt) -> opt.getFlatNumChildren()); + } + + @Override + boolean[] getFlatIsNullable() { + return super.getFlatBooleans(new boolean[]{}, (opt) -> opt.getFlatIsNullable()); + } + + @Override + String[] getFlatColumnNames() { + return super.getFlatColumnNames(new String[]{}); + } + + String[] getMetadataKeys() { + return metadata.keySet().toArray(new String[metadata.size()]); + } + + String[] getMetadataValues() { + return metadata.values().toArray(new String[metadata.size()]); + } + public enum StatisticsFrequency { /** Do not generate statistics */ NONE(0), @@ -39,32 +87,62 @@ public enum StatisticsFrequency { } } - public static class Builder extends CMWriterBuilder { + public static Builder builder() { + return new Builder(); + } + + public StatisticsFrequency getStatisticsFrequency() { + return statsGranularity; + } + + public CompressionType getCompressionType() { + return compressionType; + } + + public Map getMetadata() { + return metadata; + } + + public int getTopLevelChildren() { + return childColumnOptions.length; + } + + public static class Builder extends ParquetColumnWriterOptions.AbstractStructBuilder { private StatisticsFrequency statsGranularity = StatisticsFrequency.ROWGROUP; - private boolean isTimestampTypeInt96 = false; - private int[] precisionValues = null; + final Map metadata = new LinkedHashMap<>(); + CompressionType compressionType = CompressionType.AUTO; - public Builder withStatisticsFrequency(StatisticsFrequency statsGranularity) { - this.statsGranularity = statsGranularity; + public Builder() { + super(); + } + + /** + * Add a metadata key and a value + */ + public Builder withMetadata(String key, String value) { + this.metadata.put(key, value); return this; } /** - * Set whether the timestamps should be written in INT96 + * Add a map of metadata keys and values */ - public Builder withTimestampInt96(boolean int96) { - this.isTimestampTypeInt96 = int96; + public Builder withMetadata(Map metadata) { + this.metadata.putAll(metadata); return this; } /** - * This is a temporary hack to make things work. This API will go away once we can update the - * parquet APIs properly. - * @param precisionValues a value for each column, non-decimal columns are ignored. - * @return this for chaining. + * Set the compression type to use for writing */ - public Builder withDecimalPrecisions(int ... precisionValues) { - this.precisionValues = precisionValues; + public Builder withCompressionType(CompressionType compression) { + this.compressionType = compression; + return this; + } + + public Builder withStatisticsFrequency(StatisticsFrequency statsGranularity) { + this.statsGranularity = statsGranularity; return this; } @@ -72,40 +150,4 @@ public ParquetWriterOptions build() { return new ParquetWriterOptions(this); } } - - public static Builder builder() { - return new Builder(); - } - - private final StatisticsFrequency statsGranularity; - - private ParquetWriterOptions(Builder builder) { - super(builder); - this.statsGranularity = builder.statsGranularity; - this.isTimestampTypeInt96 = builder.isTimestampTypeInt96; - this.precisions = builder.precisionValues; - } - - public StatisticsFrequency getStatisticsFrequency() { - return statsGranularity; - } - - /** - * Return the flattened list of precisions if set otherwise empty array will be returned. - * For a definition of what `flattened` means please look at {@link Builder#withDecimalPrecisions} - */ - public int[] getPrecisions() { - return precisions; - } - - /** - * Returns true if the writer is expected to write timestamps in INT96 - */ - public boolean isTimestampTypeInt96() { - return isTimestampTypeInt96; - } - - private boolean isTimestampTypeInt96; - - private int[] precisions; } diff --git a/java/src/main/java/ai/rapids/cudf/Table.java b/java/src/main/java/ai/rapids/cudf/Table.java index a4fe3acab08..5aa35060345 100644 --- a/java/src/main/java/ai/rapids/cudf/Table.java +++ b/java/src/main/java/ai/rapids/cudf/Table.java @@ -244,6 +244,8 @@ private static native long[] readParquet(String[] filterColumnNames, String file /** * Setup everything to write parquet formatted data to a file. * @param columnNames names that correspond to the table columns + * @param numChildren Children of the top level + * @param flatNumChildren flattened list of children per column * @param nullable true if the column can have nulls else false * @param metadataKeys Metadata key names to place in the Parquet file * @param metadataValues Metadata values corresponding to metadataKeys @@ -256,18 +258,22 @@ private static native long[] readParquet(String[] filterColumnNames, String file * @return a handle that is used in later calls to writeParquetChunk and writeParquetEnd. */ private static native long writeParquetFileBegin(String[] columnNames, + int numChildren, + int[] flatNumChildren, boolean[] nullable, String[] metadataKeys, String[] metadataValues, int compression, int statsFreq, - boolean isInt96, + boolean[] isInt96, int[] precisions, String filename) throws CudfException; /** * Setup everything to write parquet formatted data to a buffer. * @param columnNames names that correspond to the table columns + * @param numChildren Children of the top level + * @param flatNumChildren flattened list of children per column * @param nullable true if the column can have nulls else false * @param metadataKeys Metadata key names to place in the Parquet file * @param metadataValues Metadata values corresponding to metadataKeys @@ -280,12 +286,14 @@ private static native long writeParquetFileBegin(String[] columnNames, * @return a handle that is used in later calls to writeParquetChunk and writeParquetEnd. */ private static native long writeParquetBufferBegin(String[] columnNames, + int numChildren, + int[] flatNumChildren, boolean[] nullable, String[] metadataKeys, String[] metadataValues, int compression, int statsFreq, - boolean isInt96, + boolean[] isInt96, int[] precisions, HostBufferConsumer consumer) throws CudfException; @@ -828,35 +836,45 @@ private static class ParquetTableWriter implements TableWriter { HostBufferConsumer consumer; private ParquetTableWriter(ParquetWriterOptions options, File outputFile) { - int numColumns = options.getColumnNames().length; - assert (numColumns == options.getColumnNullability().length); - int[] precisions = options.getPrecisions(); - if (precisions != null) { - assert (numColumns >= options.getPrecisions().length); - } + String[] columnNames = options.getFlatColumnNames(); + boolean[] columnNullabilities = options.getFlatIsNullable(); + boolean[] timeInt96Values = options.getFlatIsTimeTypeInt96(); + int[] precisions = options.getFlatPrecision(); + int[] flatNumChildren = options.getFlatNumChildren(); + this.consumer = null; - this.handle = writeParquetFileBegin(options.getColumnNames(), - options.getColumnNullability(), + this.handle = writeParquetFileBegin(columnNames, + options.getTopLevelChildren(), + flatNumChildren, + columnNullabilities, options.getMetadataKeys(), options.getMetadataValues(), options.getCompressionType().nativeId, options.getStatisticsFrequency().nativeId, - options.isTimestampTypeInt96(), - options.getPrecisions(), + timeInt96Values, + precisions, outputFile.getAbsolutePath()); } private ParquetTableWriter(ParquetWriterOptions options, HostBufferConsumer consumer) { - this.handle = writeParquetBufferBegin(options.getColumnNames(), - options.getColumnNullability(), + String[] columnNames = options.getFlatColumnNames(); + boolean[] columnNullabilities = options.getFlatIsNullable(); + boolean[] timeInt96Values = options.getFlatIsTimeTypeInt96(); + int[] precisions = options.getFlatPrecision(); + int[] flatNumChildren = options.getFlatNumChildren(); + + this.consumer = consumer; + this.handle = writeParquetBufferBegin(columnNames, + options.getTopLevelChildren(), + flatNumChildren, + columnNullabilities, options.getMetadataKeys(), options.getMetadataValues(), options.getCompressionType().nativeId, options.getStatisticsFrequency().nativeId, - options.isTimestampTypeInt96(), - options.getPrecisions(), + timeInt96Values, + precisions, consumer); - this.consumer = consumer; } @Override diff --git a/java/src/main/native/CMakeLists.txt b/java/src/main/native/CMakeLists.txt index 179a6936d8b..a8e823ea538 100755 --- a/java/src/main/native/CMakeLists.txt +++ b/java/src/main/native/CMakeLists.txt @@ -50,7 +50,9 @@ message(VERBOSE "CUDF_JNI: Statically link the CUDA runtime: ${CUDA_STATIC_RUNTI message(VERBOSE "CUDF_JNI: Build with GPUDirect Storage support: ${USE_GDS}") set(CUDF_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../../../../cpp") -set(CUDF_CPP_BUILD_DIR "${CUDF_SOURCE_DIR}/build") +if (NOT CUDF_CPP_BUILD_DIR) + set(CUDF_CPP_BUILD_DIR "${CUDF_SOURCE_DIR}/build") +endif() set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/" diff --git a/java/src/main/native/src/TableJni.cpp b/java/src/main/native/src/TableJni.cpp index c624e431477..584f7b32a02 100644 --- a/java/src/main/native/src/TableJni.cpp +++ b/java/src/main/native/src/TableJni.cpp @@ -36,10 +36,12 @@ #include #include #include +#include #include #include "cudf_jni_apis.hpp" #include "dtype_utils.hpp" +#include "jni_utils.hpp" #include "row_conversion.hpp" #include @@ -656,6 +658,67 @@ std::vector resolve_null_precedence(JNIEnv *env, jbooleanArray namespace { +int set_column_metadata(cudf::io::column_in_metadata &column_metadata, + std::vector &col_names, + cudf::jni::native_jbooleanArray &nullability, + cudf::jni::native_jbooleanArray &isInt96, + cudf::jni::native_jintArray &precisions, + cudf::jni::native_jintArray &children, int num_children, int read_index) { + int write_index = 0; + for (int i = 0; i < num_children; i++, write_index++) { + cudf::io::column_in_metadata child; + child.set_name(col_names[read_index]) + .set_decimal_precision(precisions[read_index]) + .set_int96_timestamps(isInt96[read_index]) + .set_nullability(nullability[read_index]); + column_metadata.add_child(child); + int childs_children = children[read_index++]; + if (childs_children > 0) { + read_index = set_column_metadata(column_metadata.child(write_index), col_names, nullability, + isInt96, precisions, children, childs_children, read_index); + } + } + return read_index; +} + +void createTableMetaData(JNIEnv *env, jint num_children, jobjectArray &j_col_names, jintArray &j_children, + jbooleanArray &j_col_nullability, jobjectArray &j_metadata_keys, + jobjectArray &j_metadata_values, jint j_compression, jint j_stats_freq, + jbooleanArray &j_isInt96, jintArray &j_precisions, + cudf::io::table_input_metadata& metadata) { + cudf::jni::auto_set_device(env); + cudf::jni::native_jstringArray col_names(env, j_col_names); + cudf::jni::native_jbooleanArray col_nullability(env, j_col_nullability); + cudf::jni::native_jbooleanArray isInt96(env, j_isInt96); + cudf::jni::native_jstringArray meta_keys(env, j_metadata_keys); + cudf::jni::native_jstringArray meta_values(env, j_metadata_values); + cudf::jni::native_jintArray precisions(env, j_precisions); + cudf::jni::native_jintArray children(env, j_children); + + auto cpp_names = col_names.as_cpp_vector(); + + int top_level_children = num_children; + + metadata.column_metadata.resize(top_level_children); + int read_index = 0; // the read_index, which will be used to read the arrays + for (int i = read_index, write_index = 0; i < top_level_children; i++, write_index++) { + metadata.column_metadata[write_index] + .set_name(cpp_names[read_index]) + .set_nullability(col_nullability[read_index]) + .set_int96_timestamps(isInt96[read_index]) + .set_decimal_precision(precisions[read_index]); + int childs_children = children[read_index++]; + if (childs_children > 0) { + read_index = set_column_metadata(metadata.column_metadata[write_index], cpp_names, + col_nullability, isInt96, precisions, children, childs_children, read_index); + } + } + for (auto i = 0; i < meta_keys.size(); ++i) { + metadata.user_data[meta_keys[i].get()] = meta_values[i].get(); + } + +} + // Check that window parameters are valid. bool valid_window_parameters(native_jintArray const &values, native_jpointerArray const &ops, @@ -1111,55 +1174,33 @@ JNIEXPORT jlongArray JNICALL Java_ai_rapids_cudf_Table_readParquet( } JNIEXPORT long JNICALL Java_ai_rapids_cudf_Table_writeParquetBufferBegin( - JNIEnv *env, jclass, jobjectArray j_col_names, jbooleanArray j_col_nullability, - jobjectArray j_metadata_keys, jobjectArray j_metadata_values, jint j_compression, - jint j_stats_freq, jboolean j_isInt96, jintArray j_precisions, jobject consumer) { + JNIEnv *env, jclass, jobjectArray j_col_names, jint j_num_children, jintArray j_children, + jbooleanArray j_col_nullability, jobjectArray j_metadata_keys, jobjectArray j_metadata_values, + jint j_compression, jint j_stats_freq, jbooleanArray j_isInt96, jintArray j_precisions, + jobject consumer) { JNI_NULL_CHECK(env, j_col_names, "null columns", 0); JNI_NULL_CHECK(env, j_col_nullability, "null nullability", 0); JNI_NULL_CHECK(env, j_metadata_keys, "null metadata keys", 0); JNI_NULL_CHECK(env, j_metadata_values, "null metadata values", 0); JNI_NULL_CHECK(env, consumer, "null consumer", 0); try { - cudf::jni::auto_set_device(env); - using namespace cudf::io; - cudf::jni::native_jstringArray col_names(env, j_col_names); - cudf::jni::native_jbooleanArray col_nullability(env, j_col_nullability); - cudf::jni::native_jstringArray meta_keys(env, j_metadata_keys); - cudf::jni::native_jstringArray meta_values(env, j_metadata_values); - cudf::jni::native_jintArray precisions(env, j_precisions); - - auto cpp_names = col_names.as_cpp_vector(); - table_input_metadata metadata; - metadata.column_metadata.resize(col_nullability.size()); - for (int i = 0; i < col_nullability.size(); i++) { - metadata.column_metadata[i] - .set_name(cpp_names[i]) - .set_nullability(col_nullability[i]) - .set_int96_timestamps(j_isInt96); - } - - // Precisions is not always set - for (int i = 0; i < precisions.size(); i++) { - metadata.column_metadata[i] - .set_decimal_precision(precisions[i]); - } - - for (auto i = 0; i < meta_keys.size(); ++i) { - metadata.user_data[meta_keys[i].get()] = meta_values[i].get(); - } - std::unique_ptr data_sink( new cudf::jni::jni_writer_data_sink(env, consumer)); + + using namespace cudf::io; + using namespace cudf::jni; sink_info sink{data_sink.get()}; - std::vector const v_precisions( - precisions.data(), precisions.data() + precisions.size()); + table_input_metadata metadata; + createTableMetaData(env, j_num_children, j_col_names, j_children, j_col_nullability, j_metadata_keys, + j_metadata_values, j_compression, j_stats_freq, j_isInt96, j_precisions, + metadata); + chunked_parquet_writer_options opts = chunked_parquet_writer_options::builder(sink) .metadata(&metadata) .compression(static_cast(j_compression)) .stats_level(static_cast(j_stats_freq)) .build(); - auto writer_ptr = std::make_unique(opts); cudf::jni::native_parquet_writer_handle *ret = new cudf::jni::native_parquet_writer_handle(std::move(writer_ptr), std::move(data_sink)); @@ -1169,44 +1210,23 @@ JNIEXPORT long JNICALL Java_ai_rapids_cudf_Table_writeParquetBufferBegin( } JNIEXPORT long JNICALL Java_ai_rapids_cudf_Table_writeParquetFileBegin( - JNIEnv *env, jclass, jobjectArray j_col_names, jbooleanArray j_col_nullability, - jobjectArray j_metadata_keys, jobjectArray j_metadata_values, jint j_compression, - jint j_stats_freq, jboolean j_isInt96, jintArray j_precisions, jstring j_output_path) { + JNIEnv *env, jclass, jobjectArray j_col_names, jint j_num_children, jintArray j_children, + jbooleanArray j_col_nullability, jobjectArray j_metadata_keys, jobjectArray j_metadata_values, + jint j_compression, jint j_stats_freq, jbooleanArray j_isInt96, jintArray j_precisions, + jstring j_output_path) { JNI_NULL_CHECK(env, j_col_names, "null columns", 0); JNI_NULL_CHECK(env, j_col_nullability, "null nullability", 0); JNI_NULL_CHECK(env, j_metadata_keys, "null metadata keys", 0); JNI_NULL_CHECK(env, j_metadata_values, "null metadata values", 0); JNI_NULL_CHECK(env, j_output_path, "null output path", 0); try { - cudf::jni::auto_set_device(env); - using namespace cudf::io; - cudf::jni::native_jstringArray col_names(env, j_col_names); - cudf::jni::native_jbooleanArray col_nullability(env, j_col_nullability); - cudf::jni::native_jstringArray meta_keys(env, j_metadata_keys); - cudf::jni::native_jstringArray meta_values(env, j_metadata_values); cudf::jni::native_jstring output_path(env, j_output_path); - cudf::jni::native_jintArray precisions(env, j_precisions); - auto cpp_names = col_names.as_cpp_vector(); + using namespace cudf::io; + using namespace cudf::jni; table_input_metadata metadata; - metadata.column_metadata.resize(col_nullability.size()); - for (int i = 0; i < col_nullability.size(); i++) { - metadata.column_metadata[i] - .set_name(cpp_names[i]) - .set_nullability(col_nullability[i]) - .set_int96_timestamps(j_isInt96); - } - - // Precisions is not always set - for (int i = 0; i < precisions.size(); i++) { - metadata.column_metadata[i] - .set_decimal_precision(precisions[i]); - } - - for (auto i = 0; i < meta_keys.size(); ++i) { - metadata.user_data[meta_keys[i].get()] = meta_values[i].get(); - } - + createTableMetaData(env, j_num_children, j_col_names, j_children, j_col_nullability, j_metadata_keys, + j_metadata_values, j_compression, j_stats_freq, j_isInt96, j_precisions, metadata); sink_info sink{output_path.get()}; chunked_parquet_writer_options opts = chunked_parquet_writer_options::builder(sink) @@ -1875,8 +1895,7 @@ JNIEXPORT jlongArray JNICALL Java_ai_rapids_cudf_Table_crossJoin(JNIEnv *env, jc JNIEXPORT jlong JNICALL Java_ai_rapids_cudf_Table_interleaveColumns(JNIEnv *env, jclass, jlongArray j_cudf_table_view) { - JNI_NULL_CHECK(env, j_cudf_table_view, "table is null", 0); - try { + JNI_NULL_CHECK(env, j_cudf_table_view, "table is null", 0); try { cudf::jni::auto_set_device(env); cudf::table_view *table_view = reinterpret_cast(j_cudf_table_view); std::unique_ptr result = cudf::interleave_columns(*table_view); diff --git a/java/src/test/java/ai/rapids/cudf/TableTest.java b/java/src/test/java/ai/rapids/cudf/TableTest.java index b398157983c..7991601b07a 100644 --- a/java/src/test/java/ai/rapids/cudf/TableTest.java +++ b/java/src/test/java/ai/rapids/cudf/TableTest.java @@ -50,6 +50,8 @@ import static ai.rapids.cudf.Aggregate.max; import static ai.rapids.cudf.Aggregate.first; import static ai.rapids.cudf.Aggregate.last; +import static ai.rapids.cudf.ParquetWriterOptions.listBuilder; +import static ai.rapids.cudf.ParquetWriterOptions.structBuilder; import static ai.rapids.cudf.Table.TestBuilder; import static ai.rapids.cudf.Table.count; import static ai.rapids.cudf.Table.mean; @@ -4686,36 +4688,42 @@ void testTableBasedFilter() { } private Table getExpectedFileTable() { - return getExpectedFileTable(false); + return getExpectedFileTable(false, false); } private Table getExpectedFileTable(boolean withNestedColumns) { + return getExpectedFileTable(true, true); + } + + private Table getExpectedFileTable(boolean withStructColumns, boolean withListColumn) { TestBuilder tb = new TestBuilder() - .column(true, false, false, true, false) - .column(5, 1, 0, 2, 7) - .column(new Byte[]{2, 3, 4, 5, 9}) - .column(3l, 9l, 4l, 2l, 20l) - .column("this", "is", "a", "test", "string") - .column(1.0f, 3.5f, 5.9f, 7.1f, 9.8f) - .column(5.0d, 9.5d, 0.9d, 7.23d, 2.8d); - if (withNestedColumns) { - StructType nestedType = new StructType(true, - new BasicType(false, DType.INT32), new BasicType(false, DType.STRING)); + .column(true, false, false, true, false) + .column(5, 1, 0, 2, 7) + .column(new Byte[]{2, 3, 4, 5, 9}) + .column(3l, 9l, 4l, 2l, 20l) + .column("this", "is", "a", "test", "string") + .column(1.0f, 3.5f, 5.9f, 7.1f, 9.8f) + .column(5.0d, 9.5d, 0.9d, 7.23d, 2.8d); + StructType nestedType = new StructType(true, + new BasicType(false, DType.INT32), new BasicType(false, DType.STRING)); + if (withStructColumns) { tb.column(nestedType, - struct(1, "k1"), struct(2, "k2"), struct(3, "k3"), - struct(4, "k4"), new HostColumnVector.StructData((List) null)) - .column(new ListType(false, new BasicType(false, DType.INT32)), - Arrays.asList(1, 2), - Arrays.asList(3, 4), - Arrays.asList(5), - Arrays.asList(6, 7), - Arrays.asList(8, 9, 10)) - .column(new ListType(false, nestedType), - Arrays.asList(struct(1, "k1"), struct(2, "k2"), struct(3, "k3")), - Arrays.asList(struct(4, "k4"), struct(5, "k5")), - Arrays.asList(struct(6, "k6")), - Arrays.asList(new HostColumnVector.StructData((List) null)), - Arrays.asList()); + struct(1, "k1"), struct(2, "k2"), struct(3, "k3"), + struct(4, "k4"), new HostColumnVector.StructData((List) null)); + } + if (withListColumn) { + tb.column(new ListType(false, new BasicType(false, DType.INT32)), + Arrays.asList(1, 2), + Arrays.asList(3, 4), + Arrays.asList(5), + Arrays.asList(6, 7), + Arrays.asList(8, 9, 10)) + .column(new ListType(false, nestedType), + Arrays.asList(struct(1, "k1"), struct(2, "k2"), struct(3, "k3")), + Arrays.asList(struct(4, "k4"), struct(5, "k5")), + Arrays.asList(struct(6, "k6")), + Arrays.asList(new HostColumnVector.StructData((List) null)), + Arrays.asList()); } return tb.build(); } @@ -4783,9 +4791,9 @@ void testParquetWriteToBufferChunkedInt96() { try (Table table0 = getExpectedFileTableWithDecimals(); MyBufferConsumer consumer = new MyBufferConsumer()) { ParquetWriterOptions options = ParquetWriterOptions.builder() - .withColumnNames("_c1", "_c2", "_c3", "_c4", "_c5", "_c6", "_c7", "_c8", "_c9") - .withTimestampInt96(true) - .withDecimalPrecisions(0, 0, 0, 0, 0, 0, 0, 5, 5) + .withNonNullableColumns("_c0", "_c1", "_c2", "_c3", "_c4", "_c5", "_c6") + .withDecimalColumn("_c7", 5) + .withDecimalColumn("_c8", 5) .build(); try (TableWriter writer = Table.writeParquetChunked(options, consumer)) { @@ -4800,13 +4808,47 @@ void testParquetWriteToBufferChunkedInt96() { } } + @Test + void testParquetWriteToBufferChunkedWithNested() { + ParquetWriterOptions options = ParquetWriterOptions.builder() + .withNullableColumns("_c0", "_c1", "_c2", "_c3", "_c4", "_c5", "_c6") + .withStructColumn(structBuilder("_c7") + .withNullableColumns("_c7-1") + .withNullableColumns("_c7-2") + .build()) + .withListColumn(listBuilder("_c8") + .withNullableColumns("c8-1").build()) + .withListColumn(listBuilder("c9") + .withStructColumn(structBuilder("c9-1") + .withNullableColumns("c9-1-1") + .withNullableColumns("c9-1-2").build()) + .build()) + .build(); + try (Table table0 = getExpectedFileTable(true); + MyBufferConsumer consumer = new MyBufferConsumer()) { + try (TableWriter writer = Table.writeParquetChunked(options, consumer)) { + writer.write(table0); + writer.write(table0); + writer.write(table0); + } + try (Table table1 = Table.readParquet(ParquetOptions.DEFAULT, consumer.buffer, 0, + consumer.offset); + Table concat = Table.concatenate(table0, table0, table0)) { + assertTablesAreEqual(concat, table1); + } + } + } + @Test void testParquetWriteToBufferChunked() { ParquetWriterOptions options = ParquetWriterOptions.builder() - .withColumnNames("_c1", "_c2", "_c3", "_c4", "_c5", "_c6", "_c7") - .withTimestampInt96(true) + .withNullableColumns("_c0", "_c1", "_c2", "_c3", "_c4", "_c5", "_c6") + .withStructColumn(structBuilder("_c7") + .withNullableColumns("_c7-1") + .withNullableColumns("_c7-2") + .build()) .build(); - try (Table table0 = getExpectedFileTable(); + try (Table table0 = getExpectedFileTable(true, false); MyBufferConsumer consumer = new MyBufferConsumer()) { try (TableWriter writer = Table.writeParquetChunked(options, consumer)) { writer.write(table0); @@ -4825,11 +4867,11 @@ void testParquetWriteToFileWithNames() throws IOException { File tempFile = File.createTempFile("test-names", ".parquet"); try (Table table0 = getExpectedFileTableWithDecimals()) { ParquetWriterOptions options = ParquetWriterOptions.builder() - .withColumnNames("first", "second", "third", "fourth", "fifth", "sixth", "seventh", - "eighth", "nineth") + .withNonNullableColumns("first", "second", "third", "fourth", "fifth", "sixth", "seventh") + .withDecimalColumn("eighth", 5) + .withDecimalColumn("ninth", 6) .withCompressionType(CompressionType.NONE) .withStatisticsFrequency(ParquetWriterOptions.StatisticsFrequency.NONE) - .withDecimalPrecisions(0, 0, 0, 0, 0, 0, 0, 5, 6) .build(); try (TableWriter writer = Table.writeParquetChunked(options, tempFile.getAbsoluteFile())) { writer.write(table0); @@ -4847,12 +4889,12 @@ void testParquetWriteToFileWithNamesAndMetadata() throws IOException { File tempFile = File.createTempFile("test-names-metadata", ".parquet"); try (Table table0 = getExpectedFileTableWithDecimals()) { ParquetWriterOptions options = ParquetWriterOptions.builder() - .withColumnNames("first", "second", "third", "fourth", "fifth", "sixth", "seventh", - "eighth", "nineth") + .withNonNullableColumns("first", "second", "third", "fourth", "fifth", "sixth", "seventh") + .withDecimalColumn("eighth", 6) + .withDecimalColumn("ninth", 8) .withMetadata("somekey", "somevalue") .withCompressionType(CompressionType.NONE) .withStatisticsFrequency(ParquetWriterOptions.StatisticsFrequency.NONE) - .withDecimalPrecisions(0, 0, 0, 0, 0, 0, 0, 6, 8) .build(); try (TableWriter writer = Table.writeParquetChunked(options, tempFile.getAbsoluteFile())) { writer.write(table0); @@ -4870,10 +4912,11 @@ void testParquetWriteToFileUncompressedNoStats() throws IOException { File tempFile = File.createTempFile("test-uncompressed", ".parquet"); try (Table table0 = getExpectedFileTableWithDecimals()) { ParquetWriterOptions options = ParquetWriterOptions.builder() - .withColumnNames("_c1", "_c2", "_c3", "_c4", "_c5", "_c6", "_c7", "_c8", "_c9") + .withNonNullableColumns("_c0", "_c1", "_c2", "_c3", "_c4", "_c5", "_c6") + .withDecimalColumn("_c7", 4) + .withDecimalColumn("_c8", 6) .withCompressionType(CompressionType.NONE) .withStatisticsFrequency(ParquetWriterOptions.StatisticsFrequency.NONE) - .withDecimalPrecisions(0, 0, 0, 0, 0, 0, 0, 4, 6) .build(); try (TableWriter writer = Table.writeParquetChunked(options, tempFile.getAbsoluteFile())) { writer.write(table0); From 31066794e43b431319f1f1972b9cf2a59fce2394 Mon Sep 17 00:00:00 2001 From: Mark Harris Date: Thu, 6 May 2021 08:34:05 +1000 Subject: [PATCH 42/65] Convert grouped_rolling to use device_uvector (#8106) Converts grouped_rolling to use device_uvector instead of device_vector. Contributes to #7287 I have not yet added a benchmark for grouped_rolling because I don't understand it well enough to do so. Authors: - Mark Harris (https://github.com/harrism) Approvers: - David Wendt (https://github.com/davidwendt) - Robert Maynard (https://github.com/robertmaynard) - Dillon Cullinan (https://github.com/dillon-cullinan) URL: https://github.com/rapidsai/cudf/pull/8106 --- conda/recipes/libcudf/meta.yaml | 1 + cpp/include/cudf/detail/rolling.hpp | 51 ++++++++++++++++ cpp/src/rolling/grouped_rolling.cu | 92 +++++++++++++++-------------- 3 files changed, 101 insertions(+), 43 deletions(-) create mode 100644 cpp/include/cudf/detail/rolling.hpp diff --git a/conda/recipes/libcudf/meta.yaml b/conda/recipes/libcudf/meta.yaml index 037bb70ab77..3b0267251d3 100644 --- a/conda/recipes/libcudf/meta.yaml +++ b/conda/recipes/libcudf/meta.yaml @@ -86,6 +86,7 @@ test: - test -f $PREFIX/include/cudf/detail/repeat.hpp - test -f $PREFIX/include/cudf/detail/replace.hpp - test -f $PREFIX/include/cudf/detail/reshape.hpp + - test -f $PREFIX/include/cudf/detail/rolling.hpp - test -f $PREFIX/include/cudf/detail/round.hpp - test -f $PREFIX/include/cudf/detail/scatter.hpp - test -f $PREFIX/include/cudf/detail/search.hpp diff --git a/cpp/include/cudf/detail/rolling.hpp b/cpp/include/cudf/detail/rolling.hpp new file mode 100644 index 00000000000..ec2af220440 --- /dev/null +++ b/cpp/include/cudf/detail/rolling.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include + +#include + +namespace cudf { +namespace detail { + +/** + * @copydoc std::unique_ptr rolling_window( + * column_view const& input, + * column_view const& preceding_window, + * column_view const& following_window, + * size_type min_periods, + * std::unique_ptr const& agg, + * rmm::mr::device_memory_resource* mr) + * + * @param stream CUDA stream used for device memory operations and kernel launches. + */ +std::unique_ptr rolling_window( + column_view const& input, + column_view const& preceding_window, + column_view const& following_window, + size_type min_periods, + std::unique_ptr const& agg, + rmm::cuda_stream_view stream = rmm::cuda_stream_default, + rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()); + +} // namespace detail +} // namespace cudf diff --git a/cpp/src/rolling/grouped_rolling.cu b/cpp/src/rolling/grouped_rolling.cu index 1417c22ca31..888d28fd1a5 100644 --- a/cpp/src/rolling/grouped_rolling.cu +++ b/cpp/src/rolling/grouped_rolling.cu @@ -19,12 +19,13 @@ #include "rolling_jit_detail.hpp" #include +#include +#include #include #include #include namespace cudf { - std::unique_ptr grouped_rolling_window(table_view const& group_keys, column_view const& input, size_type preceding_window, @@ -149,9 +150,8 @@ std::unique_ptr grouped_rolling_window(table_view const& group_keys, d_group_labels = group_labels.data(), following_window] __device__(size_type idx) { auto group_label = d_group_labels[idx]; - auto group_end = - d_group_offsets[group_label + - 1]; // Cannot fall off the end, since offsets is capped with `input.size()`. + auto group_end = d_group_offsets[group_label + 1]; // Cannot fall off the end, since offsets + // is capped with `input.size()`. return thrust::minimum{}(following_window, (group_end - 1) - idx); }; @@ -380,36 +380,34 @@ std::unique_ptr range_window_ASC(column_view const& input, auto following_column = expand_to_column(following_calculator, input.size(), stream, mr); - return cudf::rolling_window( - input, preceding_column->view(), following_column->view(), min_periods, aggr, mr); + return cudf::detail::rolling_window( + input, preceding_column->view(), following_column->view(), min_periods, aggr, stream, mr); } -/// Given an orderby column grouped as specified in group_offsets, -/// return the following two vectors: -/// 1. Vector with one entry per group, indicating the offset in the group -/// where the null values begin. -/// 2. Vector with one entry per group, indicating the offset in the group -/// where the null values end. (i.e. 1 past the last null.) -/// Each group in the input orderby column must be sorted, -/// with null values clustered at either the start or the end of each group. -/// If there are no nulls for any given group, (nulls_begin, nulls_end) == (0,0). -std::tuple, rmm::device_vector> +// Given an orderby column grouped as specified in group_offsets, +// return the following two vectors: +// 1. Vector with one entry per group, indicating the offset in the group +// where the null values begin. +// 2. Vector with one entry per group, indicating the offset in the group +// where the null values end. (i.e. 1 past the last null.) +// Each group in the input orderby column must be sorted, +// with null values clustered at either the start or the end of each group. +// If there are no nulls for any given group, (nulls_begin, nulls_end) == (0,0). +std::tuple, rmm::device_uvector> get_null_bounds_for_orderby_column(column_view const& orderby_column, - rmm::device_uvector const& group_offsets, + cudf::device_span group_offsets, rmm::cuda_stream_view stream) { - // For each group, the null values are themselves clustered - // at the beginning or the end of the group. + // For each group, the null values are clustered at the beginning or the end of the group. // These nulls cannot participate, except in their own window. - // If the input has n groups, group_offsets will have n+1 values. - // null_start and null_end should eventually have 1 entry per group. - auto null_start = rmm::device_vector(group_offsets.begin(), group_offsets.end() - 1); - auto null_end = rmm::device_vector(group_offsets.begin(), group_offsets.end() - 1); + auto num_groups = group_offsets.size() - 1; if (orderby_column.has_nulls()) { + auto null_start = rmm::device_uvector(num_groups, stream); + auto null_end = rmm::device_uvector(num_groups, stream); + auto p_orderby_device_view = column_device_view::create(orderby_column); - auto num_groups = group_offsets.size() - 1; // Null timestamps exist. Find null bounds, per group. thrust::for_each( @@ -450,9 +448,18 @@ get_null_bounds_for_orderby_column(column_view const& orderby_column, [&d_orderby] __device__(auto i) { return d_orderby.is_valid_nocheck(i); }); } }); - } - return std::make_tuple(std::move(null_start), std::move(null_end)); + return std::make_tuple(std::move(null_start), std::move(null_end)); + } else { + // The returned vectors have num_groups items, but the input offsets have num_groups+1 + // Drop the last element using a span + auto group_offsets_span = + cudf::device_span(group_offsets.data(), num_groups); + + // When there are no nulls, just copy the input group offsets to the output. + return std::make_tuple(cudf::detail::make_device_uvector_async(group_offsets_span, stream), + cudf::detail::make_device_uvector_async(group_offsets_span, stream)); + } } // Range window computation, for orderby column in ASCENDING order. @@ -477,8 +484,8 @@ std::unique_ptr range_window_ASC(column_view const& input, [d_group_offsets = group_offsets.data(), d_group_labels = group_labels.data(), d_orderby = orderby_column.data(), - d_nulls_begin = null_start.data().get(), - d_nulls_end = null_end.data().get(), + d_nulls_begin = null_start.data(), + d_nulls_end = null_end.data(), preceding_window, preceding_window_is_unbounded] __device__(size_type idx) -> size_type { auto group_label = d_group_labels[idx]; @@ -517,15 +524,14 @@ std::unique_ptr range_window_ASC(column_view const& input, [d_group_offsets = group_offsets.data(), d_group_labels = group_labels.data(), d_orderby = orderby_column.data(), - d_nulls_begin = null_start.data().get(), - d_nulls_end = null_end.data().get(), + d_nulls_begin = null_start.data(), + d_nulls_end = null_end.data(), following_window, following_window_is_unbounded] __device__(size_type idx) -> size_type { auto group_label = d_group_labels[idx]; auto group_start = d_group_offsets[group_label]; - auto group_end = - d_group_offsets[group_label + - 1]; // Cannot fall off the end, since offsets is capped with `input.size()`. + auto group_end = d_group_offsets[group_label + 1]; // Cannot fall off the end, since offsets + // is capped with `input.size()`. auto nulls_begin = d_nulls_begin[group_label]; auto nulls_end = d_nulls_end[group_label]; @@ -555,8 +561,8 @@ std::unique_ptr range_window_ASC(column_view const& input, auto following_column = expand_to_column(following_calculator, input.size(), stream, mr); - return cudf::rolling_window( - input, preceding_column->view(), following_column->view(), min_periods, aggr, mr); + return cudf::detail::rolling_window( + input, preceding_column->view(), following_column->view(), min_periods, aggr, stream, mr); } /// Range window computation, with @@ -647,8 +653,8 @@ std::unique_ptr range_window_DESC(column_view const& input, auto following_column = expand_to_column(following_calculator, input.size(), stream, mr); - return cudf::rolling_window( - input, preceding_column->view(), following_column->view(), min_periods, aggr, mr); + return cudf::detail::rolling_window( + input, preceding_column->view(), following_column->view(), min_periods, aggr, stream, mr); } // Range window computation, for rows in DESCENDING order. @@ -673,8 +679,8 @@ std::unique_ptr range_window_DESC(column_view const& input, [d_group_offsets = group_offsets.data(), d_group_labels = group_labels.data(), d_orderby = orderby_column.data(), - d_nulls_begin = null_start.data().get(), - d_nulls_end = null_end.data().get(), + d_nulls_begin = null_start.data(), + d_nulls_end = null_end.data(), preceding_window, preceding_window_is_unbounded] __device__(size_type idx) -> size_type { auto group_label = d_group_labels[idx]; @@ -715,8 +721,8 @@ std::unique_ptr range_window_DESC(column_view const& input, [d_group_offsets = group_offsets.data(), d_group_labels = group_labels.data(), d_orderby = orderby_column.data(), - d_nulls_begin = null_start.data().get(), - d_nulls_end = null_end.data().get(), + d_nulls_begin = null_start.data(), + d_nulls_end = null_end.data(), following_window, following_window_is_unbounded] __device__(size_type idx) -> size_type { auto group_label = d_group_labels[idx]; @@ -757,8 +763,8 @@ std::unique_ptr range_window_DESC(column_view const& input, if (aggr->kind == aggregation::CUDA || aggr->kind == aggregation::PTX) { CUDF_FAIL("Ranged rolling window does NOT (yet) support UDF."); } else { - return cudf::rolling_window( - input, preceding_column->view(), following_column->view(), min_periods, aggr, mr); + return cudf::detail::rolling_window( + input, preceding_column->view(), following_column->view(), min_periods, aggr, stream, mr); } } From 3af3bf34ef5efd09cfb4ddf011fcd8434b3fa6ba Mon Sep 17 00:00:00 2001 From: Mark Harris Date: Thu, 6 May 2021 08:34:35 +1000 Subject: [PATCH 43/65] Convert remaining uses of device_vector in groupby (#8148) Converts remaining uses of device_vector in groupby to device_uvector. Contributes to #7287. Performance on what groupby benchmarks there are is not affected much. ``` (rapids) rapids@compose:~/cudf/cpp/build/release$ _deps/benchmark-src/tools/compare.py benchmarks ~/cudf/cpp/build/groupby_before.json ~/cudf/cpp/build/groupby_after.json Comparing /home/mharris/rapids/cudf/cpp/build/groupby_before.json to /home/mharris/rapids/cudf/cpp/build/groupby_after.json Benchmark Time CPU Time Old Time New CPU Old CPU New ----------------------------------------------------------------------------------------------------------------------------------------- Groupby/Basic/10000/manual_time -0.0528 -0.0452 0 0 0 0 Groupby/Basic/10000000/manual_time +0.0089 +0.0088 3 3 3 3 Groupby/PreSorted/10000000/manual_time -0.0004 -0.0004 8 8 8 8 Groupby/PreSortedNth/1000000/manual_time -0.0045 -0.0044 0 0 0 0 Groupby/PreSortedNth/10000000/manual_time +0.0007 +0.0008 0 0 0 0 Groupby/PreSortedNth/100000000/manual_time -0.0023 -0.0023 4 4 4 4 Groupby/Shift/1000000/manual_time +0.0024 +0.0028 0 0 0 0 Groupby/Shift/10000000/manual_time -0.0048 -0.0058 3 3 3 3 Groupby/Shift/100000000/manual_time +0.0007 +0.0007 37 37 37 37 ``` Authors: - Mark Harris (https://github.com/harrism) Approvers: - Robert Maynard (https://github.com/robertmaynard) - Nghia Truong (https://github.com/ttnghia) - Ram (Ramakrishna Prabhu) (https://github.com/rgsl888prabhu) URL: https://github.com/rapidsai/cudf/pull/8148 --- cpp/src/groupby/hash/groupby.cu | 9 +++------ cpp/src/groupby/sort/group_nth_element.cu | 8 +++++++- cpp/src/groupby/sort/group_quantiles.cu | 5 +++-- cpp/src/groupby/sort/group_scan_util.cuh | 1 - 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cpp/src/groupby/hash/groupby.cu b/cpp/src/groupby/hash/groupby.cu index aced432311a..022fefb6428 100644 --- a/cpp/src/groupby/hash/groupby.cu +++ b/cpp/src/groupby/hash/groupby.cu @@ -425,17 +425,14 @@ void compute_single_pass_aggs(table_view const& keys, rmm::cuda_stream_view stream) { // flatten the aggs to a table that can be operated on by aggregate_row - table_view flattened_values; - std::vector aggs; - std::vector col_ids; - std::tie(flattened_values, aggs, col_ids) = flatten_single_pass_aggs(requests); + auto const [flattened_values, aggs, col_ids] = flatten_single_pass_aggs(requests); // make table that will hold sparse results table sparse_table = create_sparse_results_table(flattened_values, aggs, stream); // prepare to launch kernel to do the actual aggregation auto d_sparse_table = mutable_table_device_view::create(sparse_table, stream); auto d_values = table_device_view::create(flattened_values, stream); - rmm::device_vector d_aggs(aggs); + auto const d_aggs = cudf::detail::make_device_uvector_async(aggs, stream); bool skip_key_rows_with_nulls = keys_have_nulls and include_null_keys == null_policy::EXCLUDE; @@ -449,7 +446,7 @@ void compute_single_pass_aggs(table_view const& keys, keys.num_rows(), *d_values, *d_sparse_table, - d_aggs.data().get(), + d_aggs.data(), static_cast(row_bitmask.data()), skip_key_rows_with_nulls}); // Add results back to sparse_results cache diff --git a/cpp/src/groupby/sort/group_nth_element.cu b/cpp/src/groupby/sort/group_nth_element.cu index e6c10aa1056..c3d874f3b33 100644 --- a/cpp/src/groupby/sort/group_nth_element.cu +++ b/cpp/src/groupby/sort/group_nth_element.cu @@ -27,6 +27,9 @@ #include +#include +#include + namespace cudf { namespace groupby { namespace detail { @@ -45,7 +48,10 @@ std::unique_ptr group_nth_element(column_view const &values, if (num_groups == 0) { return empty_like(values); } - auto nth_index = rmm::device_vector(num_groups, values.size()); + auto nth_index = rmm::device_uvector(num_groups, stream); + // TODO: replace with async version + thrust::uninitialized_fill_n( + rmm::exec_policy(stream), nth_index.begin(), num_groups, values.size()); // nulls_policy::INCLUDE (equivalent to pandas nth(dropna=None) but return nulls for n if (null_handling == null_policy::INCLUDE || !values.has_nulls()) { diff --git a/cpp/src/groupby/sort/group_quantiles.cu b/cpp/src/groupby/sort/group_quantiles.cu index 49e14de039c..64ddc8f6b9d 100644 --- a/cpp/src/groupby/sort/group_quantiles.cu +++ b/cpp/src/groupby/sort/group_quantiles.cu @@ -21,12 +21,13 @@ #include #include #include +#include #include #include #include #include -#include +#include #include #include @@ -153,7 +154,7 @@ std::unique_ptr group_quantiles(column_view const& values, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { - rmm::device_vector dv_quantiles(quantiles); + auto dv_quantiles = cudf::detail::make_device_uvector_async(quantiles, stream); auto values_type = cudf::is_dictionary(values.type()) ? dictionary_column_view(values).keys().type() diff --git a/cpp/src/groupby/sort/group_scan_util.cuh b/cpp/src/groupby/sort/group_scan_util.cuh index 9f8614a61b4..53d05b0c48b 100644 --- a/cpp/src/groupby/sort/group_scan_util.cuh +++ b/cpp/src/groupby/sort/group_scan_util.cuh @@ -27,7 +27,6 @@ #include #include -#include #include #include From 4853dbc0ea5fa3f47fa99a5a9dfaeb2aa848f672 Mon Sep 17 00:00:00 2001 From: Ashwin Srinath <3190405+shwina@users.noreply.github.com> Date: Wed, 5 May 2021 19:17:16 -0400 Subject: [PATCH 44/65] Add a `copy()` method to `Buffer` (#8113) Closes #8102 Authors: - Ashwin Srinath (https://github.com/shwina) Approvers: - Michael Wang (https://github.com/isVoid) - GALI PREM SAGAR (https://github.com/galipremsagar) URL: https://github.com/rapidsai/cudf/pull/8113 --- python/cudf/cudf/core/buffer.py | 11 +++++++++++ python/cudf/cudf/tests/test_buffer.py | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/python/cudf/cudf/core/buffer.py b/python/cudf/cudf/core/buffer.py index 9fc5570e35a..c6875052685 100644 --- a/python/cudf/cudf/core/buffer.py +++ b/python/cudf/cudf/core/buffer.py @@ -141,6 +141,17 @@ def empty(cls, size: int) -> Buffer: dbuf = DeviceBuffer(size=size) return Buffer(dbuf) + def copy(self): + """ + Create a new Buffer containing a copy of the data contained + in this Buffer. + """ + from rmm._lib.device_buffer import copy_device_to_ptr + + out = Buffer(DeviceBuffer(size=self.size)) + copy_device_to_ptr(self.ptr, out.ptr, self.size) + return out + def _buffer_data_from_array_interface(array_interface): ptr = array_interface["data"][0] diff --git a/python/cudf/cudf/tests/test_buffer.py b/python/cudf/cudf/tests/test_buffer.py index 241d719f09e..4600d932c6f 100644 --- a/python/cudf/cudf/tests/test_buffer.py +++ b/python/cudf/cudf/tests/test_buffer.py @@ -1,5 +1,6 @@ import cupy as cp import pytest +from cupy.testing import assert_array_equal from cudf.core.buffer import Buffer @@ -44,3 +45,14 @@ def test_buffer_from_cuda_iface_dtype(data, dtype): TypeError, match="Buffer data must be of uint8 type" ): buf = Buffer(data=data, size=data.size) # noqa: F841 + + +@pytest.mark.parametrize("size", [0, 1, 10, 100, 1000, 10_000]) +def test_buffer_copy(size): + data = cp.random.randint(low=0, high=100, size=size, dtype="u1") + buf = Buffer(data=data) + got = buf.copy() + assert got.size == buf.size + if size > 0: + assert got.ptr != buf.ptr + assert_array_equal(cp.asarray(buf), cp.asarray(got)) From 52fab329f6e5a9a8e4a7c06cf03756bfabbd0587 Mon Sep 17 00:00:00 2001 From: Jason Lowe Date: Wed, 5 May 2021 19:13:48 -0500 Subject: [PATCH 45/65] Fix Java nightly build (#8169) #7998 made a change to the Java native CMakeLists.txt that breaks the Java nightly builds. Reverting that specific change to restore the build. Authors: - Jason Lowe (https://github.com/jlowe) Approvers: - Gera Shegalov (https://github.com/gerashegalov) - Raza Jafri (https://github.com/razajafri) URL: https://github.com/rapidsai/cudf/pull/8169 --- java/src/main/native/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/java/src/main/native/CMakeLists.txt b/java/src/main/native/CMakeLists.txt index a8e823ea538..179a6936d8b 100755 --- a/java/src/main/native/CMakeLists.txt +++ b/java/src/main/native/CMakeLists.txt @@ -50,9 +50,7 @@ message(VERBOSE "CUDF_JNI: Statically link the CUDA runtime: ${CUDA_STATIC_RUNTI message(VERBOSE "CUDF_JNI: Build with GPUDirect Storage support: ${USE_GDS}") set(CUDF_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../../../../cpp") -if (NOT CUDF_CPP_BUILD_DIR) - set(CUDF_CPP_BUILD_DIR "${CUDF_SOURCE_DIR}/build") -endif() +set(CUDF_CPP_BUILD_DIR "${CUDF_SOURCE_DIR}/build") set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/" From 3940e56e431870b9853e41d8869b0b9043a23702 Mon Sep 17 00:00:00 2001 From: David Wendt <45795991+davidwendt@users.noreply.github.com> Date: Wed, 5 May 2021 21:51:56 -0400 Subject: [PATCH 46/65] Split up hashing.cu to improve compile time (#8168) The `hash/hashing.cu` file takes about 8 minutes to compile on my Linux Ubuntu 18.04 machine with gcc 9.3 and nvcc 11.2. There are 3 parts to the source: MD5 hash, Murmur hash, and serial-hash (Murmur and Spark-Murmur). This PR breaks up the `hashing.cu` to 3 files improve compile time for parallel builds. Also, the MD5 hash algorithms is modified to use `std::memcpy` instead of `thrust::copy_n(thrust::seq, ...)` in device code. This provides a slight improvement in performance while also reducing the total compile time of the 3 new files to 3.5 minutes. This PR also adds a hash benchmark to the existing HASHING_BENCH to measure the current algorithms through the `cudf::hash()` API. The only new code is the `hash_benchmark.cpp` and the only changed code is the `hashing.cuh`. The rest of the source here is simply moved around to other files. Authors: - David Wendt (https://github.com/davidwendt) Approvers: - Mark Harris (https://github.com/harrism) - Vukasin Milovanovic (https://github.com/vuule) URL: https://github.com/rapidsai/cudf/pull/8168 --- cpp/CMakeLists.txt | 2 + cpp/benchmarks/CMakeLists.txt | 4 +- cpp/benchmarks/hashing/hash_benchmark.cpp | 51 ++++++ ..._benchmark.cpp => partition_benchmark.cpp} | 0 .../cudf/detail/utilities/hash_functions.cuh | 33 ++-- cpp/src/hash/hashing.cu | 171 ++---------------- cpp/src/hash/md5_hash.cu | 106 +++++++++++ cpp/src/hash/murmur_hash.cu | 84 +++++++++ 8 files changed, 280 insertions(+), 171 deletions(-) create mode 100644 cpp/benchmarks/hashing/hash_benchmark.cpp rename cpp/benchmarks/hashing/{hashing_benchmark.cpp => partition_benchmark.cpp} (100%) create mode 100644 cpp/src/hash/md5_hash.cu create mode 100644 cpp/src/hash/murmur_hash.cu diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 198690e37ff..d56207a9a40 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -211,6 +211,8 @@ add_library(cudf src/groupby/sort/group_sum_scan.cu src/groupby/sort/sort_helper.cu src/hash/hashing.cu + src/hash/md5_hash.cu + src/hash/murmur_hash.cu src/interop/dlpack.cpp src/interop/from_arrow.cu src/interop/to_arrow.cu diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index 27897143e7b..25d012b1b33 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -143,7 +143,9 @@ ConfigureBench(GROUPBY_BENCH ################################################################################################### # - hashing benchmark ----------------------------------------------------------------------------- -ConfigureBench(HASHING_BENCH hashing/hashing_benchmark.cpp) +ConfigureBench(HASHING_BENCH + hashing/hash_benchmark.cpp + hashing/partition_benchmark.cpp) ################################################################################################### # - merge benchmark ------------------------------------------------------------------------------- diff --git a/cpp/benchmarks/hashing/hash_benchmark.cpp b/cpp/benchmarks/hashing/hash_benchmark.cpp new file mode 100644 index 00000000000..77b10399693 --- /dev/null +++ b/cpp/benchmarks/hashing/hash_benchmark.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include + +class HashBenchmark : public cudf::benchmark { +}; + +static void BM_hash(benchmark::State& state, cudf::hash_id hid) +{ + cudf::size_type const n_rows{(cudf::size_type)state.range(0)}; + auto const data = create_random_table({cudf::type_id::INT64}, 1, row_count{n_rows}); + + for (auto _ : state) { + cuda_event_timer raii(state, true, rmm::cuda_stream_default); + cudf::hash(data->view(), hid); + } +} + +#define HASH_BENCHMARK_DEFINE(name) \ + BENCHMARK_DEFINE_F(HashBenchmark, name) \ + (::benchmark::State & st) { BM_hash(st, cudf::hash_id::name); } \ + BENCHMARK_REGISTER_F(HashBenchmark, name) \ + ->RangeMultiplier(4) \ + ->Ranges({{1 << 14, 1 << 24}}) \ + ->UseManualTime() \ + ->Unit(benchmark::kMillisecond); + +HASH_BENCHMARK_DEFINE(HASH_MURMUR3) +HASH_BENCHMARK_DEFINE(HASH_MD5) +HASH_BENCHMARK_DEFINE(HASH_SERIAL_MURMUR3) +HASH_BENCHMARK_DEFINE(HASH_SPARK_MURMUR3) diff --git a/cpp/benchmarks/hashing/hashing_benchmark.cpp b/cpp/benchmarks/hashing/partition_benchmark.cpp similarity index 100% rename from cpp/benchmarks/hashing/hashing_benchmark.cpp rename to cpp/benchmarks/hashing/partition_benchmark.cpp diff --git a/cpp/include/cudf/detail/utilities/hash_functions.cuh b/cpp/include/cudf/detail/utilities/hash_functions.cuh index 888a892d003..6eab13ae9af 100644 --- a/cpp/include/cudf/detail/utilities/hash_functions.cuh +++ b/cpp/include/cudf/detail/utilities/hash_functions.cuh @@ -91,21 +91,21 @@ void CUDA_DEVICE_CALLABLE md5_process(TKey const& key, md5_intermediate_data* ha // 64 bytes for the number of byt es processed in a given step constexpr int md5_chunk_size = 64; if (hash_state->buffer_length + len < md5_chunk_size) { - thrust::copy_n(thrust::seq, data, len, hash_state->buffer + hash_state->buffer_length); + std::memcpy(hash_state->buffer + hash_state->buffer_length, data, len); hash_state->buffer_length += len; } else { uint32_t copylen = md5_chunk_size - hash_state->buffer_length; - thrust::copy_n(thrust::seq, data, copylen, hash_state->buffer + hash_state->buffer_length); + std::memcpy(hash_state->buffer + hash_state->buffer_length, data, copylen); md5_hash_step(hash_state); while (len > md5_chunk_size + copylen) { - thrust::copy_n(thrust::seq, data + copylen, md5_chunk_size, hash_state->buffer); + std::memcpy(hash_state->buffer, data + copylen, md5_chunk_size); md5_hash_step(hash_state); copylen += md5_chunk_size; } - thrust::copy_n(thrust::seq, data + copylen, len - copylen, hash_state->buffer); + std::memcpy(hash_state->buffer, data + copylen, len - copylen); hash_state->buffer_length = len - copylen; } } @@ -146,7 +146,7 @@ void CUDA_DEVICE_CALLABLE uint32ToLowercaseHexString(uint32_t num, char* destina x |= 0x3030303030303030; x += offsets; - thrust::copy_n(thrust::seq, reinterpret_cast(&x), 8, destination); + std::memcpy(destination, reinterpret_cast(&x), 8); } struct MD5ListHasher { @@ -211,20 +211,20 @@ MD5ListHasher::operator()(column_device_view data_col, hash_state->message_length += len; if (hash_state->buffer_length + len < 64) { - thrust::copy_n(thrust::seq, data, len, hash_state->buffer + hash_state->buffer_length); + std::memcpy(hash_state->buffer + hash_state->buffer_length, data, len); hash_state->buffer_length += len; } else { uint32_t copylen = 64 - hash_state->buffer_length; - thrust::copy_n(thrust::seq, data, copylen, hash_state->buffer + hash_state->buffer_length); + std::memcpy(hash_state->buffer + hash_state->buffer_length, data, copylen); md5_hash_step(hash_state); while (len > 64 + copylen) { - thrust::copy_n(thrust::seq, data + copylen, 64, hash_state->buffer); + std::memcpy(hash_state->buffer, data + copylen, 64); md5_hash_step(hash_state); copylen += 64; } - thrust::copy_n(thrust::seq, data + copylen, len - copylen, hash_state->buffer); + std::memcpy(hash_state->buffer, data + copylen, len - copylen); hash_state->buffer_length = len - copylen; } } @@ -262,10 +262,9 @@ struct MD5Hash { thrust::fill_n(thrust::seq, hash_state->buffer, md5_chunk_size - message_length_size, 0x00); } - thrust::copy_n(thrust::seq, - reinterpret_cast(&full_length), - message_length_size, - hash_state->buffer + md5_chunk_size - message_length_size); + std::memcpy(hash_state->buffer + md5_chunk_size - message_length_size, + reinterpret_cast(&full_length), + message_length_size); md5_hash_step(hash_state); #pragma unroll @@ -323,20 +322,20 @@ void CUDA_DEVICE_CALLABLE MD5Hash::operator()(column_device_view co hash_state->message_length += len; if (hash_state->buffer_length + len < 64) { - thrust::copy_n(thrust::seq, data, len, hash_state->buffer + hash_state->buffer_length); + std::memcpy(hash_state->buffer + hash_state->buffer_length, data, len); hash_state->buffer_length += len; } else { uint32_t copylen = 64 - hash_state->buffer_length; - thrust::copy_n(thrust::seq, data, copylen, hash_state->buffer + hash_state->buffer_length); + std::memcpy(hash_state->buffer + hash_state->buffer_length, data, copylen); md5_hash_step(hash_state); while (len > 64 + copylen) { - thrust::copy_n(thrust::seq, data + copylen, 64, hash_state->buffer); + std::memcpy(hash_state->buffer, data + copylen, 64); md5_hash_step(hash_state); copylen += 64; } - thrust::copy_n(thrust::seq, data + copylen, len - copylen, hash_state->buffer); + std::memcpy(hash_state->buffer, data + copylen, len - copylen); hash_state->buffer_length = len - copylen; } } diff --git a/cpp/src/hash/hashing.cu b/cpp/src/hash/hashing.cu index 2bcbedd8ef3..a882b33bcdf 100644 --- a/cpp/src/hash/hashing.cu +++ b/cpp/src/hash/hashing.cu @@ -14,33 +14,23 @@ * limitations under the License. */ #include -#include -#include -#include #include #include -#include -#include #include -#include -#include #include #include -#include #include +#include + +#include #include namespace cudf { +namespace detail { namespace { -// MD5 supported leaf data type check -bool md5_type_check(data_type dt) -{ - return !is_chrono(dt) && (is_fixed_width(dt) || (dt.id() == type_id::STRING)); -} - template std::vector to_leaf_columns(IterType iter_begin, IterType iter_end) { @@ -59,89 +49,6 @@ std::vector to_leaf_columns(IterType iter_begin, IterType iter_end) } // namespace -namespace detail { - -std::unique_ptr hash(table_view const& input, - hash_id hash_function, - cudf::host_span initial_hash, - uint32_t seed, - rmm::cuda_stream_view stream, - rmm::mr::device_memory_resource* mr) -{ - switch (hash_function) { - case (hash_id::HASH_MURMUR3): return murmur_hash3_32(input, initial_hash, stream, mr); - case (hash_id::HASH_MD5): return md5_hash(input, stream, mr); - case (hash_id::HASH_SERIAL_MURMUR3): - return serial_murmur_hash3_32(input, seed, stream, mr); - case (hash_id::HASH_SPARK_MURMUR3): - return serial_murmur_hash3_32(input, seed, stream, mr); - default: return nullptr; - } -} - -std::unique_ptr md5_hash(table_view const& input, - rmm::cuda_stream_view stream, - rmm::mr::device_memory_resource* mr) -{ - if (input.num_columns() == 0 || input.num_rows() == 0) { - const string_scalar string_128bit("d41d8cd98f00b204e9orig98ecf8427e"); - auto output = make_column_from_scalar(string_128bit, input.num_rows(), stream, mr); - return output; - } - - // Accepts string and fixed width columns, or single layer list columns holding those types - CUDF_EXPECTS( - std::all_of(input.begin(), - input.end(), - [](auto col) { - return md5_type_check(col.type()) || - (col.type().id() == type_id::LIST && md5_type_check(col.child(1).type())); - }), - "MD5 unsupported column type"); - - // Result column allocation and creation - auto begin = thrust::make_constant_iterator(32); - auto offsets_column = - cudf::strings::detail::make_offsets_child_column(begin, begin + input.num_rows(), stream, mr); - - auto chars_column = - strings::detail::create_chars_child_column(input.num_rows(), input.num_rows() * 32, stream, mr); - auto chars_view = chars_column->mutable_view(); - auto d_chars = chars_view.data(); - - rmm::device_buffer null_mask{0, stream, mr}; - - auto const device_input = table_device_view::create(input, stream); - - // Hash each row, hashing each element sequentially left to right - thrust::for_each(rmm::exec_policy(stream), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(input.num_rows()), - [d_chars, device_input = *device_input] __device__(auto row_index) { - md5_intermediate_data hash_state; - MD5Hash hasher = MD5Hash{}; - for (int col_index = 0; col_index < device_input.num_columns(); col_index++) { - if (device_input.column(col_index).is_valid(row_index)) { - cudf::type_dispatcher( - device_input.column(col_index).type(), - hasher, - device_input.column(col_index), - row_index, - &hash_state); - } - } - hasher.finalize(&hash_state, d_chars + (row_index * 32)); - }); - - return make_strings_column(input.num_rows(), - std::move(offsets_column), - std::move(chars_column), - 0, - std::move(null_mask), - stream, - mr); -} - template