From 7cc6630eba55e56b9b49911837d1e94a0fc26c13 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 20 Jul 2021 19:12:02 -0700 Subject: [PATCH 01/47] initial --- cpp/include/cudf/aggregation.hpp | 1 + .../cudf/detail/aggregation/aggregation.hpp | 8 +- cpp/src/aggregation/aggregation.cpp | 1 + cpp/src/rolling/rolling_detail.cuh | 95 ++++++++++++++++++- python/cudf/cudf/_lib/aggregation.pyx | 8 ++ python/cudf/cudf/core/window/rolling.py | 3 + 6 files changed, 111 insertions(+), 5 deletions(-) diff --git a/cpp/include/cudf/aggregation.hpp b/cpp/include/cudf/aggregation.hpp index a2f59de54db..5d672cc0502 100644 --- a/cpp/include/cudf/aggregation.hpp +++ b/cpp/include/cudf/aggregation.hpp @@ -117,6 +117,7 @@ class rolling_aggregation : public virtual aggregation { protected: rolling_aggregation() {} + rolling_aggregation(aggregation::Kind a) : aggregation{a} {} }; enum class udf_type : bool { CUDA, PTX }; diff --git a/cpp/include/cudf/detail/aggregation/aggregation.hpp b/cpp/include/cudf/detail/aggregation/aggregation.hpp index 10d9d8c1b92..2642ae3a7d3 100644 --- a/cpp/include/cudf/detail/aggregation/aggregation.hpp +++ b/cpp/include/cudf/detail/aggregation/aggregation.hpp @@ -314,7 +314,7 @@ class m2_aggregation : public aggregation { /** * @brief Derived class for specifying a standard deviation/variance aggregation */ -class std_var_aggregation : public aggregation { +class std_var_aggregation : public rolling_aggregation { public: size_type _ddof; ///< Delta degrees of freedom @@ -328,7 +328,7 @@ class std_var_aggregation : public aggregation { size_t do_hash() const override { return this->aggregation::do_hash() ^ hash_impl(); } protected: - std_var_aggregation(aggregation::Kind k, size_type ddof) : aggregation(k), _ddof{ddof} + std_var_aggregation(aggregation::Kind k, size_type ddof) : rolling_aggregation(k), _ddof{ddof} { CUDF_EXPECTS(k == aggregation::STD or k == aggregation::VARIANCE, "std_var_aggregation can accept only STD, VARIANCE"); @@ -342,7 +342,7 @@ class std_var_aggregation : public aggregation { */ class var_aggregation final : public std_var_aggregation { public: - var_aggregation(size_type ddof) : std_var_aggregation{aggregation::VARIANCE, ddof} {} + var_aggregation(size_type ddof) : aggregation(aggregation::VARIANCE), std_var_aggregation{aggregation::VARIANCE, ddof} {} std::unique_ptr clone() const override { @@ -361,7 +361,7 @@ class var_aggregation final : public std_var_aggregation { */ class std_aggregation final : public std_var_aggregation { public: - std_aggregation(size_type ddof) : std_var_aggregation{aggregation::STD, ddof} {} + std_aggregation(size_type ddof) : aggregation(aggregation::STD), std_var_aggregation{aggregation::STD, ddof} {} std::unique_ptr clone() const override { diff --git a/cpp/src/aggregation/aggregation.cpp b/cpp/src/aggregation/aggregation.cpp index 53a55351f8e..d0cb2846507 100644 --- a/cpp/src/aggregation/aggregation.cpp +++ b/cpp/src/aggregation/aggregation.cpp @@ -436,6 +436,7 @@ std::unique_ptr make_std_aggregation(size_type ddof) return std::make_unique(ddof); } template std::unique_ptr make_std_aggregation(size_type ddof); +template std::unique_ptr make_std_aggregation(size_type ddof); /// Factory to create a MEDIAN aggregation template diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 862e44a0d2b..b4ebf405bf8 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -16,7 +16,10 @@ #pragma once +#include "cudf/detail/null_mask.hpp" +#include "cudf/utilities/type_dispatcher.hpp" #include "lead_lag_nested_detail.cuh" +#include "rmm/exec_policy.hpp" #include "rolling/rolling_collect_list.cuh" #include "rolling/rolling_detail.hpp" #include "rolling/rolling_jit_detail.hpp" @@ -279,6 +282,59 @@ struct DeviceRollingCountAll { } }; +/** + * @brief Operator for applying a VAR rolling aggregation on a single window. + */ + template + struct DeviceRollingVariance { + size_type min_periods; + size_type ddof; + + // what operations do we support + template + static constexpr bool is_supported() + { + return true; + } + + DeviceRollingVariance(size_type _min_periods, size_type _ddof) : min_periods(_min_periods), ddof{_ddof} {} + + template + bool __device__ operator()(column_device_view const& input, + column_device_view const&, + mutable_column_device_view& output, + size_type start_index, + size_type end_index, + size_type current_index) + { + cudf::size_type count = end_index - start_index; + + bool output_is_valid = count >= min_periods; + if (has_nulls) { + cudf::size_type null_count = thrust::count_if( + thrust::seq, thrust::make_counting_iterator(start_index), thrust::make_counting_iterator(end_index), + [&input](auto i) { return input.is_null_nocheck(i); } + ); + output_is_valid = output_is_valid and null_count == 0; + } + + OutputType mean = thrust::reduce(thrust::seq, + thrust::make_counting_iterator(start_index), + thrust::make_counting_iterator(end_index)) / count; + + OutputType result = thrust::reduce( + thrust::seq, + thrust::make_counting_iterator(start_index), + thrust::make_counting_iterator(end_index), + OutputType{0}, + [&](auto prev, auto cur) { + return prev + (cur - mean) * (cur - mean); + }) / (count - ddof); + + return output_is_valid; + } + }; + /** * @brief Operator for applying a ROW_NUMBER rolling aggregation on a single window. */ @@ -506,6 +562,16 @@ struct corresponding_rolling_operator { using type = DeviceRollingLead; }; +template +struct corresponding_rolling_operator { + using type = DeviceRollingVariance; +}; + +template +struct corresponding_rolling_operator { + using type = DeviceRollingVariance; +}; + template struct corresponding_rolling_operator { using type = DeviceRollingLag; @@ -530,12 +596,22 @@ struct create_rolling_operator< template < typename T = InputType, aggregation::Kind O = op, - std::enable_if_t* = nullptr> + std::enable_if_t* = nullptr> auto operator()(size_type min_periods, rolling_aggregation const&) { return typename corresponding_rolling_operator::type(min_periods); } + template * = nullptr> + auto operator()(size_type min_periods, rolling_aggregation const& agg) + { + return DeviceRollingVariance{ + min_periods, dynamic_cast(agg)._ddof + }; + } + template * = nullptr> @@ -750,6 +826,23 @@ class rolling_aggregation_postprocessor final : public cudf::detail::aggregation lists_column_view(collected_list->view()), agg._nulls_equal, agg._nans_equal, stream, mr); } + // perform the element-wise square root operation on result of VARIANCE + void visit(cudf::detail::std_aggregation const& agg) override + { + result = make_numeric_column(data_type{type_to_id()}, + intermediate->size(), + mask_state::UNALLOCATED, + stream, + mr); + column_view intermediate_view = intermediate->view(); + mutable_column_view result_mview = result->mutable_view(); + thrust::transform(rmm::exec_policy(stream), intermediate_view.begin(), intermediate_view.end(), result_mview.begin(), + []__device__(auto v){ return std::sqrt(v); } + ); + + result->set_null_mask(detail::copy_bitmask(intermediate_view, stream, mr), intermediate_view.null_count()); + } + std::unique_ptr get_result() { CUDF_EXPECTS(result != nullptr, diff --git a/python/cudf/cudf/_lib/aggregation.pyx b/python/cudf/cudf/_lib/aggregation.pyx index 4c94452c73d..1241c168cd2 100644 --- a/python/cudf/cudf/_lib/aggregation.pyx +++ b/python/cudf/cudf/_lib/aggregation.pyx @@ -382,6 +382,14 @@ cdef class RollingAggregation: libcudf_aggregation.make_collect_list_aggregation[ rolling_aggregation]()) return agg + + @classmethod + def std(cls, ddof=1): + cdef RollingAggregation agg = cls() + agg.c_obj = move( + libcudf_aggregation.make_std_aggregation[ + rolling_aggregation](ddof)) + return agg @classmethod def from_udf(cls, op, *args, **kwargs): diff --git a/python/cudf/cudf/core/window/rolling.py b/python/cudf/cudf/core/window/rolling.py index d9a2fd89165..dd179a83d6a 100644 --- a/python/cudf/cudf/core/window/rolling.py +++ b/python/cudf/cudf/core/window/rolling.py @@ -245,6 +245,9 @@ def mean(self): def count(self): return self._apply_agg("count") + + def std(self): + return self._apply_agg("std") def apply(self, func, *args, **kwargs): """ From 6f64691363aebac4c546f6ff38e984d7cf25cb0c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 21 Jul 2021 15:39:13 -0700 Subject: [PATCH 02/47] Compiles --- cpp/src/aggregation/aggregation.cpp | 1 + cpp/src/rolling/rolling_detail.cuh | 30 ++++---- cpp/tests/rolling/rolling_test.cpp | 107 ++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 14 deletions(-) diff --git a/cpp/src/aggregation/aggregation.cpp b/cpp/src/aggregation/aggregation.cpp index d0cb2846507..7f1b5a0c17e 100644 --- a/cpp/src/aggregation/aggregation.cpp +++ b/cpp/src/aggregation/aggregation.cpp @@ -428,6 +428,7 @@ std::unique_ptr make_variance_aggregation(size_type ddof) return std::make_unique(ddof); } template std::unique_ptr make_variance_aggregation(size_type ddof); +template std::unique_ptr make_variance_aggregation(size_type ddof); /// Factory to create a STD aggregation template diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index b4ebf405bf8..c5bad207453 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -307,9 +307,9 @@ struct DeviceRollingCountAll { size_type end_index, size_type current_index) { - cudf::size_type count = end_index - start_index; + cudf::size_type N = end_index - start_index; // population - bool output_is_valid = count >= min_periods; + bool output_is_valid = N >= min_periods and not (N == ddof); if (has_nulls) { cudf::size_type null_count = thrust::count_if( thrust::seq, thrust::make_counting_iterator(start_index), thrust::make_counting_iterator(end_index), @@ -318,18 +318,20 @@ struct DeviceRollingCountAll { output_is_valid = output_is_valid and null_count == 0; } - OutputType mean = thrust::reduce(thrust::seq, - thrust::make_counting_iterator(start_index), - thrust::make_counting_iterator(end_index)) / count; - - OutputType result = thrust::reduce( - thrust::seq, - thrust::make_counting_iterator(start_index), - thrust::make_counting_iterator(end_index), - OutputType{0}, - [&](auto prev, auto cur) { - return prev + (cur - mean) * (cur - mean); - }) / (count - ddof); + if (not (N == ddof)) { + OutputType mean = thrust::reduce(thrust::seq, + thrust::make_counting_iterator(start_index), + thrust::make_counting_iterator(end_index)) / N; + + output.element(current_index) = thrust::reduce( + thrust::seq, + thrust::make_counting_iterator(start_index), + thrust::make_counting_iterator(end_index), + OutputType{0}, + [&](auto prev, auto cur) { + return prev + (cur - mean) * (cur - mean); + }) / (N - ddof); + } return output_is_valid; } diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index a67e670acb7..2956267100d 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -29,10 +29,13 @@ #include #include #include +#include #include +#include #include +#include using cudf::bitmask_type; using cudf::size_type; @@ -247,6 +250,19 @@ class RollingTest : public cudf::test::BaseFixture { min_periods, *cudf::make_mean_aggregation()); } + + if (cudf::is_fixed_width(input.type()) and not cudf::is_chrono(input.type())) { + run_test_col(input, + preceding_window, + following_window, + min_periods, + *cudf::make_variance_aggregation()); + run_test_col(input, + preceding_window, + following_window, + min_periods, + *cudf::make_std_aggregation()); + } } private: @@ -296,6 +312,83 @@ class RollingTest : public cudf::test::BaseFixture { return col.release(); } + template() and not cudf::is_chrono())> + std::unique_ptr create_var_std_reference_output( + cudf::column_view const& input, + std::vector const& preceding_window_col, + std::vector const& following_window_col, + size_type min_periods, + size_type ddof) + { + using OutputType = double; + + size_type num_rows = input.size(); + std::vector ref_data(num_rows); + std::vector ref_valid(num_rows); + + // input data and mask + thrust::host_vector in_col; + std::vector in_valid; + std::tie(in_col, in_valid) = cudf::test::to_host(input); + bitmask_type* valid_mask = in_valid.data(); + + for (size_type i = 0; i < num_rows; i++) { + // load sizes + min_periods = std::max(min_periods, 1); // at least one observation is required + + // compute bounds + auto preceding_window = preceding_window_col[i % preceding_window_col.size()]; + auto following_window = following_window_col[i % following_window_col.size()]; + size_type start = std::min(num_rows, std::max(0, i - preceding_window + 1)); + size_type end = std::min(num_rows, std::max(0, i + following_window + 1)); + size_type start_index = std::min(start, end); + size_type end_index = std::max(start, end); + + size_type count = end_index - start_index; + + // compute var/std with raw loop - alternative to implementation + OutputType mean{0}, result{0}; + for (auto j = start_index; j < end_index; j++) { + mean += in_col[i]; + } + mean /= count; + for (auto j = start_index; j < end_index; j++) { + result += (in_col[i] - mean) * (in_col[i] - mean); + } + result /= (count - ddof); + if (do_square_root) { + result = std::sqrt(result); + } + ref_data[i] = result; + + ref_valid[i] = (count >= min_periods) and not (count == ddof); + + ref_valid[i] = ref_valid[i] and input.nullable() ? std::all_of( + thrust::make_counting_iterator(start_index), + thrust::make_counting_iterator(end_index), + [&](auto i) { return cudf::bit_is_set(valid_mask, i); } + ) : true; + } + + fixed_width_column_wrapper col(ref_data.begin(), ref_data.end(), ref_valid.begin()); + return col.release(); + } + + template() or cudf::is_chrono())> + std::unique_ptr create_var_std_reference_output( + cudf::column_view const& input, + std::vector const& preceding_window_col, + std::vector const& following_window_col, + size_type min_periods, + size_type ddof) + { + CUDF_FAIL("Unsupported combination of type and aggregation"); + } + template , true>( input, preceding_window, following_window, min_periods); + case cudf::aggregation::VARIANCE: + return create_var_std_reference_output( + input, + preceding_window, + following_window, + min_periods, + dynamic_cast(&op)->_ddof); + case cudf::aggregation::STD: + return create_var_std_reference_output( + input, + preceding_window, + following_window, + min_periods, + dynamic_cast(&op)->_ddof); default: return fixed_width_column_wrapper({}).release(); } } From 2ab88fe47723d44dad7d3831e13b018ac7a0eae7 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 22 Jul 2021 10:35:45 -0700 Subject: [PATCH 03/47] style --- .../cudf/detail/aggregation/aggregation.hpp | 10 +- cpp/src/aggregation/aggregation.cpp | 6 +- cpp/src/rolling/rolling_detail.cuh | 165 +- cpp/tests/rolling/rolling_test.cpp | 2176 +++++++++-------- 4 files changed, 1216 insertions(+), 1141 deletions(-) diff --git a/cpp/include/cudf/detail/aggregation/aggregation.hpp b/cpp/include/cudf/detail/aggregation/aggregation.hpp index 2642ae3a7d3..6997ad246ab 100644 --- a/cpp/include/cudf/detail/aggregation/aggregation.hpp +++ b/cpp/include/cudf/detail/aggregation/aggregation.hpp @@ -342,7 +342,10 @@ class std_var_aggregation : public rolling_aggregation { */ class var_aggregation final : public std_var_aggregation { public: - var_aggregation(size_type ddof) : aggregation(aggregation::VARIANCE), std_var_aggregation{aggregation::VARIANCE, ddof} {} + var_aggregation(size_type ddof) + : aggregation(aggregation::VARIANCE), std_var_aggregation{aggregation::VARIANCE, ddof} + { + } std::unique_ptr clone() const override { @@ -361,7 +364,10 @@ class var_aggregation final : public std_var_aggregation { */ class std_aggregation final : public std_var_aggregation { public: - std_aggregation(size_type ddof) : aggregation(aggregation::STD), std_var_aggregation{aggregation::STD, ddof} {} + std_aggregation(size_type ddof) + : aggregation(aggregation::STD), std_var_aggregation{aggregation::STD, ddof} + { + } std::unique_ptr clone() const override { diff --git a/cpp/src/aggregation/aggregation.cpp b/cpp/src/aggregation/aggregation.cpp index 7f1b5a0c17e..8b52aac2e29 100644 --- a/cpp/src/aggregation/aggregation.cpp +++ b/cpp/src/aggregation/aggregation.cpp @@ -428,7 +428,8 @@ std::unique_ptr make_variance_aggregation(size_type ddof) return std::make_unique(ddof); } template std::unique_ptr make_variance_aggregation(size_type ddof); -template std::unique_ptr make_variance_aggregation(size_type ddof); +template std::unique_ptr make_variance_aggregation( + size_type ddof); /// Factory to create a STD aggregation template @@ -437,7 +438,8 @@ std::unique_ptr make_std_aggregation(size_type ddof) return std::make_unique(ddof); } template std::unique_ptr make_std_aggregation(size_type ddof); -template std::unique_ptr make_std_aggregation(size_type ddof); +template std::unique_ptr make_std_aggregation( + size_type ddof); /// Factory to create a MEDIAN aggregation template diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index c5bad207453..27647fa82ea 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -285,57 +285,82 @@ struct DeviceRollingCountAll { /** * @brief Operator for applying a VAR rolling aggregation on a single window. */ - template - struct DeviceRollingVariance { - size_type min_periods; - size_type ddof; - - // what operations do we support - template - static constexpr bool is_supported() - { - return true; - } - - DeviceRollingVariance(size_type _min_periods, size_type _ddof) : min_periods(_min_periods), ddof{_ddof} {} - - template - bool __device__ operator()(column_device_view const& input, - column_device_view const&, - mutable_column_device_view& output, - size_type start_index, - size_type end_index, - size_type current_index) - { - cudf::size_type N = end_index - start_index; // population - - bool output_is_valid = N >= min_periods and not (N == ddof); - if (has_nulls) { - cudf::size_type null_count = thrust::count_if( - thrust::seq, thrust::make_counting_iterator(start_index), thrust::make_counting_iterator(end_index), - [&input](auto i) { return input.is_null_nocheck(i); } - ); - output_is_valid = output_is_valid and null_count == 0; - } - - if (not (N == ddof)) { +template +struct DeviceRollingVariance { + size_type min_periods; + size_type ddof; + + // what operations do we support + template + static constexpr bool is_supported() + { + return is_fixed_width() and not is_chrono(); + } + + DeviceRollingVariance(size_type _min_periods, size_type _ddof) + : min_periods(_min_periods), ddof{_ddof} + { + } + + template + bool __device__ operator()(column_device_view const& input, + column_device_view const&, + mutable_column_device_view& output, + size_type start_index, + size_type end_index, + size_type current_index) + { + using DeviceInputType = device_storage_type_t; + + cudf::size_type count{0}; // valid counts in the window + + if (has_nulls) { + count = thrust::count_if(thrust::seq, + thrust::make_counting_iterator(start_index), + thrust::make_counting_iterator(end_index), + [&input](auto i) { return input.is_valid_nocheck(i); }); + } else { + count = end_index - start_index; + } + + // Variance/Std is non-negative and thus ddof should be strictly less than valid counts. + bool output_is_valid = (count >= min_periods) and not(count <= ddof); + + if (output_is_valid) { OutputType mean = thrust::reduce(thrust::seq, - thrust::make_counting_iterator(start_index), - thrust::make_counting_iterator(end_index)) / N; - - output.element(current_index) = thrust::reduce( - thrust::seq, - thrust::make_counting_iterator(start_index), - thrust::make_counting_iterator(end_index), - OutputType{0}, - [&](auto prev, auto cur) { - return prev + (cur - mean) * (cur - mean); - }) / (N - ddof); + thrust::make_counting_iterator(start_index), + thrust::make_counting_iterator(end_index), + OutputType{0}, + [&](auto acc, auto i) { + if (has_nulls) { + return input.is_valid_nocheck(i) + ? acc + input.element(i) + : acc; + } else { + return acc + input.element(i); + } + }) / + count; + + output.element(current_index) = + thrust::reduce(thrust::seq, + thrust::make_counting_iterator(start_index), + thrust::make_counting_iterator(end_index), + OutputType{0}, + [&](auto acc, auto i) { + auto x = input.element(i); + if (has_nulls) { + return input.is_valid_nocheck(i) ? acc + (x - mean) * (x - mean) : acc; + } else { + return acc + (x - mean) * (x - mean); + } + }) / + (count - ddof); } - return output_is_valid; - } - }; + return output_is_valid; + } +}; /** * @brief Operator for applying a ROW_NUMBER rolling aggregation on a single window. @@ -596,22 +621,23 @@ struct create_rolling_operator< op, std::enable_if_t::type::is_supported()>> { template < - typename T = InputType, - aggregation::Kind O = op, - std::enable_if_t* = nullptr> + typename T = InputType, + aggregation::Kind O = op, + std::enable_if_t* = nullptr> auto operator()(size_type min_periods, rolling_aggregation const&) { return typename corresponding_rolling_operator::type(min_periods); } - template * = nullptr> + template < + typename T = InputType, + aggregation::Kind O = op, + std::enable_if_t* = nullptr> auto operator()(size_type min_periods, rolling_aggregation const& agg) { return DeviceRollingVariance{ - min_periods, dynamic_cast(agg)._ddof - }; + min_periods, dynamic_cast(agg)._ddof}; } template ()}, - intermediate->size(), - mask_state::UNALLOCATED, - stream, - mr); - column_view intermediate_view = intermediate->view(); + result = make_numeric_column( + data_type{type_to_id()}, intermediate->size(), mask_state::UNALLOCATED, stream, mr); + column_view intermediate_view = intermediate->view(); mutable_column_view result_mview = result->mutable_view(); - thrust::transform(rmm::exec_policy(stream), intermediate_view.begin(), intermediate_view.end(), result_mview.begin(), - []__device__(auto v){ return std::sqrt(v); } - ); - - result->set_null_mask(detail::copy_bitmask(intermediate_view, stream, mr), intermediate_view.null_count()); + thrust::transform(rmm::exec_policy(stream), + intermediate_view.begin(), + intermediate_view.end(), + result_mview.begin(), + [] __device__(auto v) { return std::sqrt(v); }); + + result->set_null_mask(detail::copy_bitmask(intermediate_view, stream, mr), + intermediate_view.null_count()); } std::unique_ptr get_result() @@ -1085,6 +1111,9 @@ struct dispatch_rolling { rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { + if constexpr (is_fixed_width() and not is_chrono()) + cudf::debug::print>(input, std::cout, ",", stream.value()); + // do any preprocessing of aggregations (eg, MIN -> ARGMIN, COLLECT_LIST -> nothing) rolling_aggregation_preprocessor preprocessor; auto preprocessed_aggs = agg.get_simple_aggregations(input.type(), preprocessor); @@ -1106,6 +1135,8 @@ struct dispatch_rolling { mr) : nullptr; + cudf::debug::print(intermediate->view(), std::cout, ",", stream.value()); + // finalize. auto const result_type = target_type(input.type(), agg.kind); rolling_aggregation_postprocessor postprocessor(input, diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 2956267100d..33cccbfc302 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -28,140 +28,140 @@ #include #include #include -#include #include +#include #include #include -#include #include +#include using cudf::bitmask_type; using cudf::size_type; using cudf::test::fixed_width_column_wrapper; -class RollingStringTest : public cudf::test::BaseFixture { -}; - -TEST_F(RollingStringTest, NoNullStringMinMaxCount) -{ - cudf::test::strings_column_wrapper input( - {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}); - std::vector window{2}; - cudf::test::strings_column_wrapper expected_min( - {"This", "This", "being", "being", "being", "being", "column", "column", "column"}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - cudf::test::strings_column_wrapper expected_max( - {"rolling", "test", "test", "test", "test", "string", "string", "string", "string"}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - fixed_width_column_wrapper expected_count({3, 4, 4, 4, 4, 4, 4, 3, 2}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - - auto got_min = cudf::rolling_window( - input, window[0], window[0], 1, *cudf::make_min_aggregation()); - auto got_max = cudf::rolling_window( - input, window[0], window[0], 1, *cudf::make_max_aggregation()); - auto got_count_valid = cudf::rolling_window( - input, window[0], window[0], 1, *cudf::make_count_aggregation()); - auto got_count_all = cudf::rolling_window( - input, - window[0], - window[0], - 1, - *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count_valid->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count_all->view()); -} - -TEST_F(RollingStringTest, NullStringMinMaxCount) -{ - cudf::test::strings_column_wrapper input( - {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, - {1, 0, 0, 1, 0, 1, 1, 1, 0}); - std::vector window{2}; - cudf::test::strings_column_wrapper expected_min( - {"This", "This", "test", "operated", "on", "on", "on", "on", "string"}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - cudf::test::strings_column_wrapper expected_max( - {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 1}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - - auto got_min = cudf::rolling_window( - input, window[0], window[0], 1, *cudf::make_min_aggregation()); - auto got_max = cudf::rolling_window( - input, window[0], window[0], 1, *cudf::make_max_aggregation()); - auto got_count_valid = cudf::rolling_window( - input, window[0], window[0], 1, *cudf::make_count_aggregation()); - auto got_count_all = cudf::rolling_window( - input, - window[0], - window[0], - 1, - *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); -} - -TEST_F(RollingStringTest, MinPeriods) -{ - cudf::test::strings_column_wrapper input( - {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, - {1, 0, 0, 1, 0, 1, 1, 1, 0}); - std::vector window{2}; - cudf::test::strings_column_wrapper expected_min( - {"This", "This", "This", "operated", "on", "on", "on", "on", "on"}, - {0, 0, 0, 0, 1, 1, 1, 0, 0}); - cudf::test::strings_column_wrapper expected_max( - {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, - {0, 0, 0, 0, 1, 1, 1, 0, 0}); - fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 2}, - {1, 1, 1, 1, 1, 1, 1, 1, 0}); - fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, - {0, 1, 1, 1, 1, 1, 1, 0, 0}); - - auto got_min = cudf::rolling_window( - input, window[0], window[0], 3, *cudf::make_min_aggregation()); - auto got_max = cudf::rolling_window( - input, window[0], window[0], 3, *cudf::make_max_aggregation()); - auto got_count_valid = cudf::rolling_window( - input, window[0], window[0], 3, *cudf::make_count_aggregation()); - auto got_count_all = cudf::rolling_window( - input, - window[0], - window[0], - 4, - *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); -} - -TEST_F(RollingStringTest, ZeroWindowSize) -{ - cudf::test::strings_column_wrapper input( - {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, - {1, 0, 0, 1, 0, 1, 1, 1, 0}); - fixed_width_column_wrapper expected_count({0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); - - auto got_count = cudf::rolling_window( - input, 0, 0, 0, *cudf::make_count_aggregation()); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count->view()); -} +// class RollingStringTest : public cudf::test::BaseFixture { +// }; + +// TEST_F(RollingStringTest, NoNullStringMinMaxCount) +// { +// cudf::test::strings_column_wrapper input( +// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}); +// std::vector window{2}; +// cudf::test::strings_column_wrapper expected_min( +// {"This", "This", "being", "being", "being", "being", "column", "column", "column"}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); +// cudf::test::strings_column_wrapper expected_max( +// {"rolling", "test", "test", "test", "test", "string", "string", "string", "string"}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); +// fixed_width_column_wrapper expected_count({3, 4, 4, 4, 4, 4, 4, 3, 2}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); + +// auto got_min = cudf::rolling_window( +// input, window[0], window[0], 1, *cudf::make_min_aggregation()); +// auto got_max = cudf::rolling_window( +// input, window[0], window[0], 1, *cudf::make_max_aggregation()); +// auto got_count_valid = cudf::rolling_window( +// input, window[0], window[0], 1, *cudf::make_count_aggregation()); +// auto got_count_all = cudf::rolling_window( +// input, +// window[0], +// window[0], +// 1, +// *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count_valid->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count_all->view()); +// } + +// TEST_F(RollingStringTest, NullStringMinMaxCount) +// { +// cudf::test::strings_column_wrapper input( +// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, +// {1, 0, 0, 1, 0, 1, 1, 1, 0}); +// std::vector window{2}; +// cudf::test::strings_column_wrapper expected_min( +// {"This", "This", "test", "operated", "on", "on", "on", "on", "string"}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); +// cudf::test::strings_column_wrapper expected_max( +// {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); +// fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 1}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); +// fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); + +// auto got_min = cudf::rolling_window( +// input, window[0], window[0], 1, *cudf::make_min_aggregation()); +// auto got_max = cudf::rolling_window( +// input, window[0], window[0], 1, *cudf::make_max_aggregation()); +// auto got_count_valid = cudf::rolling_window( +// input, window[0], window[0], 1, *cudf::make_count_aggregation()); +// auto got_count_all = cudf::rolling_window( +// input, +// window[0], +// window[0], +// 1, +// *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); +// } + +// TEST_F(RollingStringTest, MinPeriods) +// { +// cudf::test::strings_column_wrapper input( +// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, +// {1, 0, 0, 1, 0, 1, 1, 1, 0}); +// std::vector window{2}; +// cudf::test::strings_column_wrapper expected_min( +// {"This", "This", "This", "operated", "on", "on", "on", "on", "on"}, +// {0, 0, 0, 0, 1, 1, 1, 0, 0}); +// cudf::test::strings_column_wrapper expected_max( +// {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, +// {0, 0, 0, 0, 1, 1, 1, 0, 0}); +// fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 2}, +// {1, 1, 1, 1, 1, 1, 1, 1, 0}); +// fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, +// {0, 1, 1, 1, 1, 1, 1, 0, 0}); + +// auto got_min = cudf::rolling_window( +// input, window[0], window[0], 3, *cudf::make_min_aggregation()); +// auto got_max = cudf::rolling_window( +// input, window[0], window[0], 3, *cudf::make_max_aggregation()); +// auto got_count_valid = cudf::rolling_window( +// input, window[0], window[0], 3, *cudf::make_count_aggregation()); +// auto got_count_all = cudf::rolling_window( +// input, +// window[0], +// window[0], +// 4, +// *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); +// } + +// TEST_F(RollingStringTest, ZeroWindowSize) +// { +// cudf::test::strings_column_wrapper input( +// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, +// {1, 0, 0, 1, 0, 1, 1, 1, 0}); +// fixed_width_column_wrapper expected_count({0, 0, 0, 0, 0, 0, 0, 0, 0}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); + +// auto got_count = cudf::rolling_window( +// input, 0, 0, 0, *cudf::make_count_aggregation()); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count->view()); +// } template class RollingTest : public cudf::test::BaseFixture { @@ -216,52 +216,53 @@ class RollingTest : public cudf::test::BaseFixture { size_type min_periods) { // test all supported aggregators - run_test_col(input, - preceding_window, - following_window, - min_periods, - *cudf::make_min_aggregation()); - run_test_col(input, - preceding_window, - following_window, - min_periods, - *cudf::make_count_aggregation()); - run_test_col( - input, - preceding_window, - following_window, - min_periods, - *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); - run_test_col(input, - preceding_window, - following_window, - min_periods, - *cudf::make_max_aggregation()); - - if (not cudf::is_timestamp(input.type())) { - run_test_col(input, - preceding_window, - following_window, - min_periods, - *cudf::make_sum_aggregation()); - run_test_col(input, - preceding_window, - following_window, - min_periods, - *cudf::make_mean_aggregation()); - } + // run_test_col(input, + // preceding_window, + // following_window, + // min_periods, + // *cudf::make_min_aggregation()); + // run_test_col(input, + // preceding_window, + // following_window, + // min_periods, + // *cudf::make_count_aggregation()); + // run_test_col( + // input, + // preceding_window, + // following_window, + // min_periods, + // *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); + // run_test_col(input, + // preceding_window, + // following_window, + // min_periods, + // *cudf::make_max_aggregation()); + + // if (not cudf::is_timestamp(input.type())) { + // run_test_col(input, + // preceding_window, + // following_window, + // min_periods, + // *cudf::make_sum_aggregation()); + // run_test_col(input, + // preceding_window, + // following_window, + // min_periods, + // *cudf::make_mean_aggregation()); + // } if (cudf::is_fixed_width(input.type()) and not cudf::is_chrono(input.type())) { + std::cout << "Running variance test\n"; run_test_col(input, preceding_window, following_window, min_periods, *cudf::make_variance_aggregation()); - run_test_col(input, - preceding_window, - following_window, - min_periods, - *cudf::make_std_aggregation()); + // run_test_col(input, + // preceding_window, + // following_window, + // min_periods, + // *cudf::make_std_aggregation()); } } @@ -312,9 +313,9 @@ class RollingTest : public cudf::test::BaseFixture { return col.release(); } - template() and not cudf::is_chrono())> + template () and not cudf::is_chrono())> std::unique_ptr create_var_std_reference_output( cudf::column_view const& input, std::vector const& preceding_window_col, @@ -322,6 +323,16 @@ class RollingTest : public cudf::test::BaseFixture { size_type min_periods, size_type ddof) { + std::cout << "ddof " << ddof << std::endl; + std::cout << "preceding_window_col:\n"; + for (auto x : preceding_window_col) { + std::cout << x; + } + std::cout << "\nfollowing_window_col:\n"; + for (auto x : following_window_col) { + std::cout << x; + } + std::cout << "\n"; using OutputType = double; size_type num_rows = input.size(); @@ -346,39 +357,40 @@ class RollingTest : public cudf::test::BaseFixture { size_type start_index = std::min(start, end); size_type end_index = std::max(start, end); - size_type count = end_index - start_index; - - // compute var/std with raw loop - alternative to implementation + // compute window var/std + size_type count{0}; // valid count in window OutputType mean{0}, result{0}; for (auto j = start_index; j < end_index; j++) { - mean += in_col[i]; + if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { count++; } } - mean /= count; - for (auto j = start_index; j < end_index; j++) { - result += (in_col[i] - mean) * (in_col[i] - mean); - } - result /= (count - ddof); - if (do_square_root) { - result = std::sqrt(result); + if (count >= min_periods and not(count == ddof)) { + for (auto j = start_index; j < end_index; j++) { + if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { mean += in_col[j]; } + } + mean /= count; + for (auto j = start_index; j < end_index; j++) { + if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { + result += (in_col[j] - mean) * (in_col[j] - mean); + } + } + result /= (end_index - start_index - ddof); + if (do_square_root) { result = std::sqrt(result); } } - ref_data[i] = result; - - ref_valid[i] = (count >= min_periods) and not (count == ddof); - - ref_valid[i] = ref_valid[i] and input.nullable() ? std::all_of( - thrust::make_counting_iterator(start_index), - thrust::make_counting_iterator(end_index), - [&](auto i) { return cudf::bit_is_set(valid_mask, i); } - ) : true; + ref_data[i] = result; + ref_valid[i] = (count >= min_periods) and not(count == ddof); + + std::cout << count << " " << min_periods << " " << (count >= min_periods) + << static_cast(ref_valid[i]) << std::endl; } fixed_width_column_wrapper col(ref_data.begin(), ref_data.end(), ref_valid.begin()); + cudf::test::print(col); return col.release(); } - template() or cudf::is_chrono())> + template () or cudf::is_chrono())> std::unique_ptr create_var_std_reference_output( cudf::column_view const& input, std::vector const& preceding_window_col, @@ -389,74 +401,75 @@ class RollingTest : public cudf::test::BaseFixture { CUDF_FAIL("Unsupported combination of type and aggregation"); } - template ()>* = nullptr> - std::unique_ptr create_reference_output( - cudf::column_view const& input, - std::vector const& preceding_window_col, - std::vector const& following_window_col, - size_type min_periods) - { - size_type num_rows = input.size(); - thrust::host_vector ref_data(num_rows); - thrust::host_vector ref_valid(num_rows); - - // input data and mask - thrust::host_vector in_col; - std::vector in_valid; - std::tie(in_col, in_valid) = cudf::test::to_host(input); - bitmask_type* valid_mask = in_valid.data(); - - agg_op op; - for (size_type i = 0; i < num_rows; i++) { - OutputType val = agg_op::template identity(); - - // load sizes - min_periods = std::max(min_periods, 1); // at least one observation is required - - // compute bounds - auto preceding_window = preceding_window_col[i % preceding_window_col.size()]; - auto following_window = following_window_col[i % following_window_col.size()]; - size_type start = std::min(num_rows, std::max(0, i - preceding_window + 1)); - size_type end = std::min(num_rows, std::max(0, i + following_window + 1)); - size_type start_index = std::min(start, end); - size_type end_index = std::max(start, end); - - // aggregate - size_type count = 0; - for (size_type j = start_index; j < end_index; j++) { - if (!input.nullable() || cudf::bit_is_set(valid_mask, j)) { - val = op(static_cast(in_col[j]), val); - count++; - } - } - - ref_valid[i] = (count >= min_periods); - if (ref_valid[i]) { - cudf::detail::rolling_store_output_functor{}(ref_data[i], val, count); - } - } - - fixed_width_column_wrapper col(ref_data.begin(), ref_data.end(), ref_valid.begin()); - return col.release(); - } - - template ()>* = nullptr> - std::unique_ptr create_reference_output( - cudf::column_view const& input, - std::vector const& preceding_window_col, - std::vector const& following_window_col, - size_type min_periods) - { - CUDF_FAIL("Unsupported combination of type and aggregation"); - } + // template ()>* = nullptr> + // std::unique_ptr create_reference_output( + // cudf::column_view const& input, + // std::vector const& preceding_window_col, + // std::vector const& following_window_col, + // size_type min_periods) + // { + // size_type num_rows = input.size(); + // thrust::host_vector ref_data(num_rows); + // thrust::host_vector ref_valid(num_rows); + + // // input data and mask + // thrust::host_vector in_col; + // std::vector in_valid; + // std::tie(in_col, in_valid) = cudf::test::to_host(input); + // bitmask_type* valid_mask = in_valid.data(); + + // agg_op op; + // for (size_type i = 0; i < num_rows; i++) { + // OutputType val = agg_op::template identity(); + + // // load sizes + // min_periods = std::max(min_periods, 1); // at least one observation is required + + // // compute bounds + // auto preceding_window = preceding_window_col[i % preceding_window_col.size()]; + // auto following_window = following_window_col[i % following_window_col.size()]; + // size_type start = std::min(num_rows, std::max(0, i - preceding_window + 1)); + // size_type end = std::min(num_rows, std::max(0, i + following_window + 1)); + // size_type start_index = std::min(start, end); + // size_type end_index = std::max(start, end); + + // // aggregate + // size_type count = 0; + // for (size_type j = start_index; j < end_index; j++) { + // if (!input.nullable() || cudf::bit_is_set(valid_mask, j)) { + // val = op(static_cast(in_col[j]), val); + // count++; + // } + // } + + // ref_valid[i] = (count >= min_periods); + // if (ref_valid[i]) { + // cudf::detail::rolling_store_output_functor{}(ref_data[i], val, + // count); + // } + // } + + // fixed_width_column_wrapper col(ref_data.begin(), ref_data.end(), + // ref_valid.begin()); return col.release(); + // } + + // template ()>* = nullptr> + // std::unique_ptr create_reference_output( + // cudf::column_view const& input, + // std::vector const& preceding_window_col, + // std::vector const& following_window_col, + // size_type min_periods) + // { + // CUDF_FAIL("Unsupported combination of type and aggregation"); + // } std::unique_ptr create_reference_output( cudf::rolling_aggregation const& op, @@ -467,50 +480,50 @@ class RollingTest : public cudf::test::BaseFixture { { // unroll aggregation types switch (op.kind) { - case cudf::aggregation::SUM: - return create_reference_output, - false>( - input, preceding_window, following_window, min_periods); - case cudf::aggregation::MIN: - return create_reference_output, - false>( - input, preceding_window, following_window, min_periods); - case cudf::aggregation::MAX: - return create_reference_output, - false>( - input, preceding_window, following_window, min_periods); - case cudf::aggregation::COUNT_VALID: - return create_count_reference_output( - input, preceding_window, following_window, min_periods); - case cudf::aggregation::COUNT_ALL: - return create_count_reference_output( - input, preceding_window, following_window, min_periods); - case cudf::aggregation::MEAN: - return create_reference_output, - true>( - input, preceding_window, following_window, min_periods); + // case cudf::aggregation::SUM: + // return create_reference_output, + // false>( + // input, preceding_window, following_window, min_periods); + // case cudf::aggregation::MIN: + // return create_reference_output, + // false>( + // input, preceding_window, following_window, min_periods); + // case cudf::aggregation::MAX: + // return create_reference_output, + // false>( + // input, preceding_window, following_window, min_periods); + // case cudf::aggregation::COUNT_VALID: + // return create_count_reference_output( + // input, preceding_window, following_window, min_periods); + // case cudf::aggregation::COUNT_ALL: + // return create_count_reference_output( + // input, preceding_window, following_window, min_periods); + // case cudf::aggregation::MEAN: + // return create_reference_output, + // true>( + // input, preceding_window, following_window, min_periods); case cudf::aggregation::VARIANCE: return create_var_std_reference_output( input, preceding_window, following_window, min_periods, - dynamic_cast(&op)->_ddof); + dynamic_cast(&op)->_ddof); case cudf::aggregation::STD: return create_var_std_reference_output( input, preceding_window, following_window, min_periods, - dynamic_cast(&op)->_ddof); + dynamic_cast(&op)->_ddof); default: return fixed_width_column_wrapper({}).release(); } } @@ -518,801 +531,824 @@ class RollingTest : public cudf::test::BaseFixture { // // ------------- expected failures -------------------- -class RollingErrorTest : public cudf::test::BaseFixture { -}; - -// negative sizes -TEST_F(RollingErrorTest, NegativeMinPeriods) -{ - const std::vector col_data = {0, 1, 2, 0, 4}; - const std::vector col_valid = {1, 1, 1, 0, 1}; - fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_valid.begin()); - - EXPECT_THROW( - cudf::rolling_window(input, 2, 2, -2, *cudf::make_sum_aggregation()), - cudf::logic_error); -} - -// window array size mismatch -TEST_F(RollingErrorTest, WindowArraySizeMismatch) -{ - const std::vector col_data = {0, 1, 2, 0, 4}; - const std::vector col_valid = {1, 1, 1, 0, 1}; - fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_valid.begin()); - - std::vector five({2, 1, 2, 1, 4}); - std::vector four({1, 2, 3, 4}); - fixed_width_column_wrapper five_elements(five.begin(), five.end()); - fixed_width_column_wrapper four_elements(four.begin(), four.end()); - - // this runs ok - EXPECT_NO_THROW(cudf::rolling_window(input, - five_elements, - five_elements, - 1, - *cudf::make_sum_aggregation())); - - // mismatch for the window array - EXPECT_THROW(cudf::rolling_window(input, - four_elements, - five_elements, - 1, - *cudf::make_sum_aggregation()), - cudf::logic_error); - - // mismatch for the forward window array - EXPECT_THROW(cudf::rolling_window(input, - five_elements, - four_elements, - 1, - *cudf::make_sum_aggregation()), - cudf::logic_error); -} - -TEST_F(RollingErrorTest, EmptyInput) -{ - cudf::test::fixed_width_column_wrapper empty_col{}; - std::unique_ptr output; - EXPECT_NO_THROW(output = cudf::rolling_window( - empty_col, 2, 0, 2, *cudf::make_sum_aggregation())); - EXPECT_EQ(output->size(), 0); - - fixed_width_column_wrapper preceding_window{}; - fixed_width_column_wrapper following_window{}; - EXPECT_NO_THROW(output = - cudf::rolling_window(empty_col, - preceding_window, - following_window, - 2, - *cudf::make_sum_aggregation())); - EXPECT_EQ(output->size(), 0); - - fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; - EXPECT_NO_THROW(output = - cudf::rolling_window(nonempty_col, - preceding_window, - following_window, - 2, - *cudf::make_sum_aggregation())); - EXPECT_EQ(output->size(), 0); -} - -TEST_F(RollingErrorTest, SizeMismatch) -{ - fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; - std::unique_ptr output; - - { - fixed_width_column_wrapper preceding_window{{1, 1}}; // wrong size - fixed_width_column_wrapper following_window{{1, 1, 1}}; - EXPECT_THROW( - output = cudf::rolling_window(nonempty_col, - preceding_window, - following_window, - 2, - *cudf::make_sum_aggregation()), - cudf::logic_error); - } - { - fixed_width_column_wrapper preceding_window{{1, 1, 1}}; - fixed_width_column_wrapper following_window{{1, 2}}; // wrong size - EXPECT_THROW( - output = cudf::rolling_window(nonempty_col, - preceding_window, - following_window, - 2, - *cudf::make_sum_aggregation()), - cudf::logic_error); - } -} - -TEST_F(RollingErrorTest, WindowWrongDtype) -{ - fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; - std::unique_ptr output; - - fixed_width_column_wrapper preceding_window{{1.0f, 1.0f, 1.0f}}; - fixed_width_column_wrapper following_window{{1.0f, 1.0f, 1.0f}}; - EXPECT_THROW( - output = cudf::rolling_window(nonempty_col, - preceding_window, - following_window, - 2, - *cudf::make_sum_aggregation()), - cudf::logic_error); -} - -// incorrect type/aggregation combo: sum of timestamps -TEST_F(RollingErrorTest, SumTimestampNotSupported) -{ - constexpr size_type size{10}; - fixed_width_column_wrapper input_D( - thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - fixed_width_column_wrapper input_s( - thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - fixed_width_column_wrapper input_ms( - thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - fixed_width_column_wrapper input_us( - thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - fixed_width_column_wrapper input_ns( - thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - - EXPECT_THROW(cudf::rolling_window( - input_D, 2, 2, 0, *cudf::make_sum_aggregation()), - cudf::logic_error); - EXPECT_THROW(cudf::rolling_window( - input_s, 2, 2, 0, *cudf::make_sum_aggregation()), - cudf::logic_error); - EXPECT_THROW(cudf::rolling_window( - input_ms, 2, 2, 0, *cudf::make_sum_aggregation()), - cudf::logic_error); - EXPECT_THROW(cudf::rolling_window( - input_us, 2, 2, 0, *cudf::make_sum_aggregation()), - cudf::logic_error); - EXPECT_THROW(cudf::rolling_window( - input_ns, 2, 2, 0, *cudf::make_sum_aggregation()), - cudf::logic_error); -} - -// incorrect type/aggregation combo: mean of timestamps -TEST_F(RollingErrorTest, MeanTimestampNotSupported) -{ - constexpr size_type size{10}; - fixed_width_column_wrapper input_D( - thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - fixed_width_column_wrapper input_s( - thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - fixed_width_column_wrapper input_ms( - thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - fixed_width_column_wrapper input_us( - thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - fixed_width_column_wrapper input_ns( - thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - - EXPECT_THROW(cudf::rolling_window( - input_D, 2, 2, 0, *cudf::make_mean_aggregation()), - cudf::logic_error); - EXPECT_THROW(cudf::rolling_window( - input_s, 2, 2, 0, *cudf::make_mean_aggregation()), - cudf::logic_error); - EXPECT_THROW(cudf::rolling_window( - input_ms, 2, 2, 0, *cudf::make_mean_aggregation()), - cudf::logic_error); - EXPECT_THROW(cudf::rolling_window( - input_us, 2, 2, 0, *cudf::make_mean_aggregation()), - cudf::logic_error); - EXPECT_THROW(cudf::rolling_window( - input_ns, 2, 2, 0, *cudf::make_mean_aggregation()), - cudf::logic_error); -} +// class RollingErrorTest : public cudf::test::BaseFixture { +// }; + +// // negative sizes +// TEST_F(RollingErrorTest, NegativeMinPeriods) +// { +// const std::vector col_data = {0, 1, 2, 0, 4}; +// const std::vector col_valid = {1, 1, 1, 0, 1}; +// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), +// col_valid.begin()); + +// EXPECT_THROW( +// cudf::rolling_window(input, 2, 2, -2, +// *cudf::make_sum_aggregation()), cudf::logic_error); +// } + +// // window array size mismatch +// TEST_F(RollingErrorTest, WindowArraySizeMismatch) +// { +// const std::vector col_data = {0, 1, 2, 0, 4}; +// const std::vector col_valid = {1, 1, 1, 0, 1}; +// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), +// col_valid.begin()); + +// std::vector five({2, 1, 2, 1, 4}); +// std::vector four({1, 2, 3, 4}); +// fixed_width_column_wrapper five_elements(five.begin(), five.end()); +// fixed_width_column_wrapper four_elements(four.begin(), four.end()); + +// // this runs ok +// EXPECT_NO_THROW(cudf::rolling_window(input, +// five_elements, +// five_elements, +// 1, +// *cudf::make_sum_aggregation())); + +// // mismatch for the window array +// EXPECT_THROW(cudf::rolling_window(input, +// four_elements, +// five_elements, +// 1, +// *cudf::make_sum_aggregation()), +// cudf::logic_error); + +// // mismatch for the forward window array +// EXPECT_THROW(cudf::rolling_window(input, +// five_elements, +// four_elements, +// 1, +// *cudf::make_sum_aggregation()), +// cudf::logic_error); +// } + +// TEST_F(RollingErrorTest, EmptyInput) +// { +// cudf::test::fixed_width_column_wrapper empty_col{}; +// std::unique_ptr output; +// EXPECT_NO_THROW(output = cudf::rolling_window( +// empty_col, 2, 0, 2, +// *cudf::make_sum_aggregation())); +// EXPECT_EQ(output->size(), 0); + +// fixed_width_column_wrapper preceding_window{}; +// fixed_width_column_wrapper following_window{}; +// EXPECT_NO_THROW(output = +// cudf::rolling_window(empty_col, +// preceding_window, +// following_window, +// 2, +// *cudf::make_sum_aggregation())); +// EXPECT_EQ(output->size(), 0); + +// fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; +// EXPECT_NO_THROW(output = +// cudf::rolling_window(nonempty_col, +// preceding_window, +// following_window, +// 2, +// *cudf::make_sum_aggregation())); +// EXPECT_EQ(output->size(), 0); +// } + +// TEST_F(RollingErrorTest, SizeMismatch) +// { +// fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; +// std::unique_ptr output; + +// { +// fixed_width_column_wrapper preceding_window{{1, 1}}; // wrong size +// fixed_width_column_wrapper following_window{{1, 1, 1}}; +// EXPECT_THROW( +// output = cudf::rolling_window(nonempty_col, +// preceding_window, +// following_window, +// 2, +// *cudf::make_sum_aggregation()), +// cudf::logic_error); +// } +// { +// fixed_width_column_wrapper preceding_window{{1, 1, 1}}; +// fixed_width_column_wrapper following_window{{1, 2}}; // wrong size +// EXPECT_THROW( +// output = cudf::rolling_window(nonempty_col, +// preceding_window, +// following_window, +// 2, +// *cudf::make_sum_aggregation()), +// cudf::logic_error); +// } +// } + +// TEST_F(RollingErrorTest, WindowWrongDtype) +// { +// fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; +// std::unique_ptr output; + +// fixed_width_column_wrapper preceding_window{{1.0f, 1.0f, 1.0f}}; +// fixed_width_column_wrapper following_window{{1.0f, 1.0f, 1.0f}}; +// EXPECT_THROW( +// output = cudf::rolling_window(nonempty_col, +// preceding_window, +// following_window, +// 2, +// *cudf::make_sum_aggregation()), +// cudf::logic_error); +// } + +// // incorrect type/aggregation combo: sum of timestamps +// TEST_F(RollingErrorTest, SumTimestampNotSupported) +// { +// constexpr size_type size{10}; +// fixed_width_column_wrapper input_D( +// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); +// fixed_width_column_wrapper input_s( +// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); +// fixed_width_column_wrapper input_ms( +// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); +// fixed_width_column_wrapper input_us( +// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); +// fixed_width_column_wrapper input_ns( +// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + +// EXPECT_THROW(cudf::rolling_window( +// input_D, 2, 2, 0, *cudf::make_sum_aggregation()), +// cudf::logic_error); +// EXPECT_THROW(cudf::rolling_window( +// input_s, 2, 2, 0, *cudf::make_sum_aggregation()), +// cudf::logic_error); +// EXPECT_THROW(cudf::rolling_window( +// input_ms, 2, 2, 0, *cudf::make_sum_aggregation()), +// cudf::logic_error); +// EXPECT_THROW(cudf::rolling_window( +// input_us, 2, 2, 0, *cudf::make_sum_aggregation()), +// cudf::logic_error); +// EXPECT_THROW(cudf::rolling_window( +// input_ns, 2, 2, 0, *cudf::make_sum_aggregation()), +// cudf::logic_error); +// } + +// // incorrect type/aggregation combo: mean of timestamps +// TEST_F(RollingErrorTest, MeanTimestampNotSupported) +// { +// constexpr size_type size{10}; +// fixed_width_column_wrapper input_D( +// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); +// fixed_width_column_wrapper input_s( +// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); +// fixed_width_column_wrapper input_ms( +// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); +// fixed_width_column_wrapper input_us( +// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); +// fixed_width_column_wrapper input_ns( +// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + +// EXPECT_THROW(cudf::rolling_window( +// input_D, 2, 2, 0, *cudf::make_mean_aggregation()), +// cudf::logic_error); +// EXPECT_THROW(cudf::rolling_window( +// input_s, 2, 2, 0, *cudf::make_mean_aggregation()), +// cudf::logic_error); +// EXPECT_THROW(cudf::rolling_window( +// input_ms, 2, 2, 0, *cudf::make_mean_aggregation()), +// cudf::logic_error); +// EXPECT_THROW(cudf::rolling_window( +// input_us, 2, 2, 0, *cudf::make_mean_aggregation()), +// cudf::logic_error); +// EXPECT_THROW(cudf::rolling_window( +// input_ns, 2, 2, 0, *cudf::make_mean_aggregation()), +// cudf::logic_error); +// } TYPED_TEST_CASE(RollingTest, cudf::test::FixedWidthTypesWithoutFixedPoint); -// simple example from Pandas docs -TYPED_TEST(RollingTest, SimpleStatic) +// // simple example from Pandas docs +// TYPED_TEST(RollingTest, SimpleStatic) +// { +// // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html +// auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, +// 4}); const std::vector col_mask = {1, 1, 1, 0, 1}; + +// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), +// col_mask.begin()); std::vector window{2}; + +// // static sizes +// this->run_test_col_agg(input, window, window, 1); +// } + +// // negative sizes +// TYPED_TEST(RollingTest, NegativeWindowSizes) +// { +// auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, 4}); +// auto const col_valid = std::vector{1, 1, 1, 0, 1}; +// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), +// col_valid.begin()); std::vector window{3}; std::vector +// negative_window{-2}; + +// this->run_test_col_agg(input, negative_window, window, 1); +// this->run_test_col_agg(input, window, negative_window, 1); +// this->run_test_col_agg(input, negative_window, negative_window, 1); +// } + +// // simple example from Pandas docs: +// TYPED_TEST(RollingTest, SimpleDynamic) +// { +// // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html +// auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, +// 4}); const std::vector col_mask = {1, 1, 1, 0, 1}; + +// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), +// col_mask.begin()); std::vector preceding_window({1, 2, 3, 4, 2}); +// std::vector following_window({2, 1, 2, 1, 2}); + +// // dynamic sizes +// this->run_test_col_agg(input, preceding_window, following_window, 1); +// } + +// // this is a special test to check the volatile count variable issue (see rolling.cu for detail) +// TYPED_TEST(RollingTest, VolatileCount) +// { +// auto const col_data = cudf::test::make_type_param_vector({8, 70, 45, 20, 59, 80}); +// const std::vector col_mask = {1, 1, 0, 0, 1, 0}; + +// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), +// col_mask.begin()); std::vector preceding_window({5, 9, 4, 8, 3, 3}); +// std::vector following_window({1, 1, 9, 2, 8, 9}); + +// // dynamic sizes +// this->run_test_col_agg(input, preceding_window, following_window, 1); +// } + +// // all rows are invalid +// TYPED_TEST(RollingTest, AllInvalid) +// { +// size_type num_rows = 1000; + +// std::vector col_data(num_rows); +// std::vector col_mask(num_rows, 0); + +// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), +// col_mask.begin()); std::vector window({100}); size_type periods = 100; + +// this->run_test_col_agg(input, window, window, periods); +// } + +// // window = following_window = 0 +// TYPED_TEST(RollingTest, ZeroWindow) +// { +// size_type num_rows = 1000; + +// std::vector col_data(num_rows, 1); +// std::vector col_mask(num_rows, 1); + +// fixed_width_column_wrapper input( +// col_data.begin(), col_data.end(), col_mask.begin()); +// std::vector window({0}); +// size_type periods = num_rows; + +// this->run_test_col_agg(input, window, window, periods); +// } + +// // min_periods = 0 +// TYPED_TEST(RollingTest, ZeroPeriods) +// { +// size_type num_rows = 1000; + +// std::vector col_data(num_rows, 1); +// std::vector col_mask(num_rows, 1); + +// fixed_width_column_wrapper input( +// col_data.begin(), col_data.end(), col_mask.begin()); + +// std::vector window({num_rows}); +// size_type periods = 0; + +// this->run_test_col_agg(input, window, window, periods); +// } + +// // window in one direction is not large enough to collect enough samples, +// // but if using both directions we should get == min_periods, +// // also tests out of boundary accesses +// TYPED_TEST(RollingTest, BackwardForwardWindow) +// { +// size_type num_rows = 1000; + +// std::vector col_data(num_rows, 1); +// std::vector col_mask(num_rows, 1); + +// fixed_width_column_wrapper input( +// col_data.begin(), col_data.end(), col_mask.begin()); + +// std::vector window({num_rows}); +// size_type periods = num_rows; + +// this->run_test_col_agg(input, window, window, periods); +// } + +// // random input data, static parameters, no nulls +// TYPED_TEST(RollingTest, RandomStaticAllValid) +// { +// size_type num_rows = 10000; + +// // random input +// std::vector col_data(num_rows); +// cudf::test::UniformRandomGenerator rng; +// std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); +// fixed_width_column_wrapper input(col_data.begin(), col_data.end()); + +// std::vector window({50}); +// size_type periods = 50; + +// this->run_test_col_agg(input, window, window, periods); +// } + +// // random input data, static parameters, with nulls +// TYPED_TEST(RollingTest, RandomStaticWithInvalid) +// { +// size_type num_rows = 10000; + +// // random input +// std::vector col_data(num_rows); +// std::vector col_valid(num_rows); +// cudf::test::UniformRandomGenerator rng; +// cudf::test::UniformRandomGenerator rbg; +// std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); +// std::generate(col_valid.begin(), col_valid.end(), [&rbg]() { return rbg.generate(); }); +// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), +// col_valid.begin()); + +// std::vector window({50}); +// size_type periods = 50; + +// this->run_test_col_agg(input, window, window, periods); +// } + +// // random input data, dynamic parameters, no nulls +// TYPED_TEST(RollingTest, RandomDynamicAllValid) +// { +// size_type num_rows = 50000; +// size_type max_window_size = 50; + +// // random input +// std::vector col_data(num_rows); +// cudf::test::UniformRandomGenerator rng; +// std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); +// fixed_width_column_wrapper input(col_data.begin(), col_data.end()); + +// // random parameters +// cudf::test::UniformRandomGenerator window_rng(0, max_window_size); +// auto generator = [&]() { return window_rng.generate(); }; + +// std::vector preceding_window(num_rows); +// std::vector following_window(num_rows); + +// std::generate(preceding_window.begin(), preceding_window.end(), generator); +// std::generate(following_window.begin(), following_window.end(), generator); + +// this->run_test_col_agg(input, preceding_window, following_window, max_window_size); +// } + +// // random input data, dynamic parameters, with nulls +// TYPED_TEST(RollingTest, RandomDynamicWithInvalid) +// { +// size_type num_rows = 50000; +// size_type max_window_size = 50; + +// // random input with nulls +// std::vector col_data(num_rows); +// std::vector col_valid(num_rows); +// cudf::test::UniformRandomGenerator rng; +// cudf::test::UniformRandomGenerator rbg; +// std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); +// std::generate(col_valid.begin(), col_valid.end(), [&rbg]() { return rbg.generate(); }); +// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), +// col_valid.begin()); + +// // random parameters +// cudf::test::UniformRandomGenerator window_rng(0, max_window_size); +// auto generator = [&]() { return window_rng.generate(); }; + +// std::vector preceding_window(num_rows); +// std::vector following_window(num_rows); + +// std::generate(preceding_window.begin(), preceding_window.end(), generator); +// std::generate(following_window.begin(), following_window.end(), generator); + +// this->run_test_col_agg(input, preceding_window, following_window, max_window_size); +// } + +TYPED_TEST(RollingTest, ContrivedVar) { // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, 4}); - const std::vector col_mask = {1, 1, 1, 0, 1}; + const std::vector col_mask = {1, 1, 1, 1, 1}; fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); - std::vector window{2}; + std::vector pwindow{2}; + std::vector fwindow{0}; // static sizes - this->run_test_col_agg(input, window, window, 1); -} - -// negative sizes -TYPED_TEST(RollingTest, NegativeWindowSizes) -{ - auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, 4}); - auto const col_valid = std::vector{1, 1, 1, 0, 1}; - fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_valid.begin()); - std::vector window{3}; - std::vector negative_window{-2}; - - this->run_test_col_agg(input, negative_window, window, 1); - this->run_test_col_agg(input, window, negative_window, 1); - this->run_test_col_agg(input, negative_window, negative_window, 1); -} - -// simple example from Pandas docs: -TYPED_TEST(RollingTest, SimpleDynamic) -{ - // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html - auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, 4}); - const std::vector col_mask = {1, 1, 1, 0, 1}; - - fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); - std::vector preceding_window({1, 2, 3, 4, 2}); - std::vector following_window({2, 1, 2, 1, 2}); - - // dynamic sizes - this->run_test_col_agg(input, preceding_window, following_window, 1); -} - -// this is a special test to check the volatile count variable issue (see rolling.cu for detail) -TYPED_TEST(RollingTest, VolatileCount) -{ - auto const col_data = cudf::test::make_type_param_vector({8, 70, 45, 20, 59, 80}); - const std::vector col_mask = {1, 1, 0, 0, 1, 0}; - - fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); - std::vector preceding_window({5, 9, 4, 8, 3, 3}); - std::vector following_window({1, 1, 9, 2, 8, 9}); - - // dynamic sizes - this->run_test_col_agg(input, preceding_window, following_window, 1); -} - -// all rows are invalid -TYPED_TEST(RollingTest, AllInvalid) -{ - size_type num_rows = 1000; - - std::vector col_data(num_rows); - std::vector col_mask(num_rows, 0); - - fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); - std::vector window({100}); - size_type periods = 100; - - this->run_test_col_agg(input, window, window, periods); -} - -// window = following_window = 0 -TYPED_TEST(RollingTest, ZeroWindow) -{ - size_type num_rows = 1000; - - std::vector col_data(num_rows, 1); - std::vector col_mask(num_rows, 1); - - fixed_width_column_wrapper input( - col_data.begin(), col_data.end(), col_mask.begin()); - std::vector window({0}); - size_type periods = num_rows; - - this->run_test_col_agg(input, window, window, periods); -} - -// min_periods = 0 -TYPED_TEST(RollingTest, ZeroPeriods) -{ - size_type num_rows = 1000; - - std::vector col_data(num_rows, 1); - std::vector col_mask(num_rows, 1); - - fixed_width_column_wrapper input( - col_data.begin(), col_data.end(), col_mask.begin()); - - std::vector window({num_rows}); - size_type periods = 0; - - this->run_test_col_agg(input, window, window, periods); -} - -// window in one direction is not large enough to collect enough samples, -// but if using both directions we should get == min_periods, -// also tests out of boundary accesses -TYPED_TEST(RollingTest, BackwardForwardWindow) -{ - size_type num_rows = 1000; - - std::vector col_data(num_rows, 1); - std::vector col_mask(num_rows, 1); - - fixed_width_column_wrapper input( - col_data.begin(), col_data.end(), col_mask.begin()); - - std::vector window({num_rows}); - size_type periods = num_rows; - - this->run_test_col_agg(input, window, window, periods); -} - -// random input data, static parameters, no nulls -TYPED_TEST(RollingTest, RandomStaticAllValid) -{ - size_type num_rows = 10000; - - // random input - std::vector col_data(num_rows); - cudf::test::UniformRandomGenerator rng; - std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); - fixed_width_column_wrapper input(col_data.begin(), col_data.end()); - - std::vector window({50}); - size_type periods = 50; - - this->run_test_col_agg(input, window, window, periods); -} - -// random input data, static parameters, with nulls -TYPED_TEST(RollingTest, RandomStaticWithInvalid) -{ - size_type num_rows = 10000; - - // random input - std::vector col_data(num_rows); - std::vector col_valid(num_rows); - cudf::test::UniformRandomGenerator rng; - cudf::test::UniformRandomGenerator rbg; - std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); - std::generate(col_valid.begin(), col_valid.end(), [&rbg]() { return rbg.generate(); }); - fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_valid.begin()); - - std::vector window({50}); - size_type periods = 50; - - this->run_test_col_agg(input, window, window, periods); -} - -// random input data, dynamic parameters, no nulls -TYPED_TEST(RollingTest, RandomDynamicAllValid) -{ - size_type num_rows = 50000; - size_type max_window_size = 50; - - // random input - std::vector col_data(num_rows); - cudf::test::UniformRandomGenerator rng; - std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); - fixed_width_column_wrapper input(col_data.begin(), col_data.end()); - - // random parameters - cudf::test::UniformRandomGenerator window_rng(0, max_window_size); - auto generator = [&]() { return window_rng.generate(); }; - - std::vector preceding_window(num_rows); - std::vector following_window(num_rows); - - std::generate(preceding_window.begin(), preceding_window.end(), generator); - std::generate(following_window.begin(), following_window.end(), generator); - - this->run_test_col_agg(input, preceding_window, following_window, max_window_size); -} - -// random input data, dynamic parameters, with nulls -TYPED_TEST(RollingTest, RandomDynamicWithInvalid) -{ - size_type num_rows = 50000; - size_type max_window_size = 50; - - // random input with nulls - std::vector col_data(num_rows); - std::vector col_valid(num_rows); - cudf::test::UniformRandomGenerator rng; - cudf::test::UniformRandomGenerator rbg; - std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); - std::generate(col_valid.begin(), col_valid.end(), [&rbg]() { return rbg.generate(); }); - fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_valid.begin()); - - // random parameters - cudf::test::UniformRandomGenerator window_rng(0, max_window_size); - auto generator = [&]() { return window_rng.generate(); }; - - std::vector preceding_window(num_rows); - std::vector following_window(num_rows); - - std::generate(preceding_window.begin(), preceding_window.end(), generator); - std::generate(following_window.begin(), following_window.end(), generator); - - this->run_test_col_agg(input, preceding_window, following_window, max_window_size); + this->run_test_col_agg(input, pwindow, fwindow, 2); } // ------------- non-fixed-width types -------------------- -using RollingTestStrings = RollingTest; - -TEST_F(RollingTestStrings, StringsUnsupportedOperators) -{ - cudf::test::strings_column_wrapper input{{"This", "is", "not", "a", "string", "type"}, - {1, 1, 1, 0, 1, 0}}; - - std::vector window{1}; - - EXPECT_THROW( - cudf::rolling_window(input, 2, 2, 0, *cudf::make_sum_aggregation()), - cudf::logic_error); - EXPECT_THROW( - cudf::rolling_window(input, 2, 2, 0, *cudf::make_mean_aggregation()), - cudf::logic_error); - EXPECT_THROW(cudf::rolling_window(input, - 2, - 2, - 0, - *cudf::make_udf_aggregation( - cudf::udf_type::PTX, std::string{}, cudf::data_type{})), - cudf::logic_error); - EXPECT_THROW(cudf::rolling_window(input, - 2, - 2, - 0, - *cudf::make_udf_aggregation( - cudf::udf_type::CUDA, std::string{}, cudf::data_type{})), - cudf::logic_error); -} - -/*TEST_F(RollingTestStrings, SimpleStatic) -{ - cudf::test::strings_column_wrapper input{{"This", "is", "not", "a", "string", "type"}, - {1, 1, 1, 0, 1, 0}}; - - std::vector window{1}; - - EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::MIN)); - EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::MAX)); - EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::COUNT_VALID)); - EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::COUNT_ALL)); -}*/ - -struct RollingTestUdf : public cudf::test::BaseFixture { - const std::string cuda_func{ - R"***( - template - __device__ void CUDA_GENERIC_AGGREGATOR(OutType *ret, InType *in_col, cudf::size_type start, - cudf::size_type count) { - OutType val = 0; - for (cudf::size_type i = 0; i < count; i++) { - val += in_col[start + i]; - } - *ret = val; - } - )***"}; - - const std::string ptx_func{ - R"***( - // - // Generated by NVIDIA NVVM Compiler - // - // Compiler Build ID: CL-24817639 - // Cuda compilation tools, release 10.0, V10.0.130 - // Based on LLVM 3.4svn - // - - .version 6.3 - .target sm_70 - .address_size 64 - - // .globl _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE - .common .global .align 8 .u64 _ZN08NumbaEnv8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE; - - .visible .func (.param .b32 func_retval0) _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE( - .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0, - .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_1, - .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_2, - .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_3, - .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_4, - .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5, - .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6, - .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_7 - ) - { - .reg .pred %p<3>; - .reg .b32 %r<6>; - .reg .b64 %rd<18>; - - - ld.param.u64 %rd6, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0]; - ld.param.u64 %rd7, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5]; - ld.param.u64 %rd8, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6]; - ld.param.u64 %rd9, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_7]; - mov.u64 %rd15, 0; - mov.u64 %rd16, %rd15; - - BB0_1: - mov.u64 %rd2, %rd16; - mov.u32 %r5, 0; - setp.ge.s64 %p1, %rd15, %rd8; - mov.u64 %rd17, %rd15; - @%p1 bra BB0_3; - - mul.lo.s64 %rd12, %rd15, %rd9; - add.s64 %rd13, %rd12, %rd7; - ld.u32 %r5, [%rd13]; - add.s64 %rd17, %rd15, 1; - - BB0_3: - cvt.s64.s32 %rd14, %r5; - add.s64 %rd16, %rd14, %rd2; - setp.lt.s64 %p2, %rd15, %rd8; - mov.u64 %rd15, %rd17; - @%p2 bra BB0_1; - - st.u64 [%rd6], %rd2; - mov.u32 %r4, 0; - st.param.b32 [func_retval0+0], %r4; - ret; - } - )***"}; -}; - -TEST_F(RollingTestUdf, StaticWindow) -{ - size_type size = 1000; - - fixed_width_column_wrapper input(thrust::make_counting_iterator(0), - thrust::make_counting_iterator(size), - thrust::make_constant_iterator(true)); - - std::unique_ptr output; - - auto start = cudf::detail::make_counting_transform_iterator(0, [size](size_type row) { - return std::accumulate(thrust::make_counting_iterator(std::max(0, row - 2 + 1)), - thrust::make_counting_iterator(std::min(size, row + 2 + 1)), - 0); - }); - - auto valid = cudf::detail::make_counting_transform_iterator( - 0, [size](size_type row) { return (row != 0 && row != size - 2 && row != size - 1); }); - - fixed_width_column_wrapper expected{start, start + size, valid}; - - // Test CUDA UDF - auto cuda_udf_agg = cudf::make_udf_aggregation( - cudf::udf_type::CUDA, this->cuda_func, cudf::data_type{cudf::type_id::INT64}); - - output = cudf::rolling_window(input, 2, 2, 4, *cuda_udf_agg); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); - - // Test NUMBA UDF - auto ptx_udf_agg = cudf::make_udf_aggregation( - cudf::udf_type::PTX, this->ptx_func, cudf::data_type{cudf::type_id::INT64}); - - output = cudf::rolling_window(input, 2, 2, 4, *ptx_udf_agg); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); -} - -TEST_F(RollingTestUdf, DynamicWindow) -{ - size_type size = 1000; - - fixed_width_column_wrapper input(thrust::make_counting_iterator(0), - thrust::make_counting_iterator(size), - thrust::make_constant_iterator(true)); - - auto prec = cudf::detail::make_counting_transform_iterator( - 0, [size] __device__(size_type row) { return row % 2 + 2; }); - - auto follow = cudf::detail::make_counting_transform_iterator( - 0, [size] __device__(size_type row) { return row % 2; }); - - fixed_width_column_wrapper preceding(prec, prec + size); - fixed_width_column_wrapper following(follow, follow + size); - std::unique_ptr output; - - auto start = cudf::detail::make_counting_transform_iterator(0, [size] __device__(size_type row) { - return std::accumulate(thrust::make_counting_iterator(std::max(0, row - (row % 2 + 2) + 1)), - thrust::make_counting_iterator(std::min(size, row + (row % 2) + 1)), - 0); - }); - - auto valid = cudf::detail::make_counting_transform_iterator( - 0, [size] __device__(size_type row) { return row != 0; }); - - fixed_width_column_wrapper expected{start, start + size, valid}; - - // Test CUDA UDF - auto cuda_udf_agg = cudf::make_udf_aggregation( - cudf::udf_type::CUDA, this->cuda_func, cudf::data_type{cudf::type_id::INT64}); - - output = cudf::rolling_window(input, preceding, following, 2, *cuda_udf_agg); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); - - // Test PTX UDF - auto ptx_udf_agg = cudf::make_udf_aggregation( - cudf::udf_type::PTX, this->ptx_func, cudf::data_type{cudf::type_id::INT64}); - - output = cudf::rolling_window(input, preceding, following, 2, *ptx_udf_agg); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); -} - -template -struct FixedPointTests : public cudf::test::BaseFixture { -}; - -TYPED_TEST_CASE(FixedPointTests, cudf::test::FixedPointTypes); - -TYPED_TEST(FixedPointTests, MinMaxCountLagLead) -{ - using namespace numeric; - using namespace cudf; - using decimalXX = TypeParam; - using RepType = cudf::device_storage_type_t; - using fp_wrapper = cudf::test::fixed_point_column_wrapper; - using fw_wrapper = cudf::test::fixed_width_column_wrapper; - - auto const scale = scale_type{-1}; - auto const input = fp_wrapper{{42, 1729, 55, 3, 1, 2}, {1, 1, 1, 1, 1, 1}, scale}; - auto const expected_min = fp_wrapper{{42, 42, 3, 1, 1, 1}, {1, 1, 1, 1, 1, 1}, scale}; - auto const expected_max = fp_wrapper{{1729, 1729, 1729, 55, 3, 2}, {1, 1, 1, 1, 1, 1}, scale}; - auto const expected_lag = fp_wrapper{{0, 42, 1729, 55, 3, 1}, {0, 1, 1, 1, 1, 1}, scale}; - auto const expected_lead = fp_wrapper{{1729, 55, 3, 1, 2, 0}, {1, 1, 1, 1, 1, 0}, scale}; - auto const expected_count_val = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; - auto const expected_count_all = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; - auto const expected_rowno = fw_wrapper{{1, 2, 2, 2, 2, 2}, {1, 1, 1, 1, 1, 1}}; - auto const expected_rowno1 = fw_wrapper{{1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1}}; - - auto const min = - rolling_window(input, 2, 1, 1, *make_min_aggregation()); - auto const max = - rolling_window(input, 2, 1, 1, *make_max_aggregation()); - auto const lag = - rolling_window(input, 2, 1, 1, *make_lag_aggregation(1)); - auto const lead = - rolling_window(input, 2, 1, 1, *make_lead_aggregation(1)); - auto const valid = - rolling_window(input, 2, 1, 1, *make_count_aggregation()); - auto const all = rolling_window( - input, 2, 1, 1, *make_count_aggregation(null_policy::INCLUDE)); - auto const rowno = - rolling_window(input, 2, 1, 1, *make_row_number_aggregation()); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, min->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, max->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, lag->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, lead->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, valid->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, all->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno, rowno->view()); - - // ROW_NUMBER will always return row 1 if the preceding window is set to a constant 1 - for (int following = 1; following < 5; ++following) { - auto const rowno1 = rolling_window( - input, 1, following, 1, *make_row_number_aggregation()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno1, rowno1->view()); - } -} - -TYPED_TEST(FixedPointTests, MinMaxCountLagLeadNulls) -{ - using namespace numeric; - using namespace cudf; - using decimalXX = TypeParam; - using RepType = cudf::device_storage_type_t; - using fp_wrapper = cudf::test::fixed_point_column_wrapper; - using fp64_wrapper = cudf::test::fixed_point_column_wrapper; - using fw_wrapper = cudf::test::fixed_width_column_wrapper; - - auto const scale = scale_type{-1}; - auto const input = fp_wrapper{{42, 1729, 55, 343, 1, 2}, {1, 0, 1, 0, 1, 1}, scale}; - auto const expected_sum = fp64_wrapper{{42, 97, 55, 56, 3, 3}, {1, 1, 1, 1, 1, 1}, scale}; - auto const expected_min = fp_wrapper{{42, 42, 55, 1, 1, 1}, {1, 1, 1, 1, 1, 1}, scale}; - auto const expected_max = fp_wrapper{{42, 55, 55, 55, 2, 2}, {1, 1, 1, 1, 1, 1}, scale}; - auto const expected_lag = fp_wrapper{{0, 42, 1729, 55, 343, 1}, {0, 1, 0, 1, 0, 1}, scale}; - auto const expected_lead = fp_wrapper{{1729, 55, 343, 1, 2, 0}, {0, 1, 0, 1, 1, 0}, scale}; - auto const expected_count_val = fw_wrapper{{1, 2, 1, 2, 2, 2}, {1, 1, 1, 1, 1, 1}}; - auto const expected_count_all = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; - auto const expected_rowno = fw_wrapper{{1, 2, 2, 2, 2, 2}, {1, 1, 1, 1, 1, 1}}; - - auto const sum = - rolling_window(input, 2, 1, 1, *make_sum_aggregation()); - auto const min = - rolling_window(input, 2, 1, 1, *make_min_aggregation()); - auto const max = - rolling_window(input, 2, 1, 1, *make_max_aggregation()); - auto const lag = - rolling_window(input, 2, 1, 1, *make_lag_aggregation(1)); - auto const lead = - rolling_window(input, 2, 1, 1, *make_lead_aggregation(1)); - auto const valid = - rolling_window(input, 2, 1, 1, *make_count_aggregation()); - auto const all = rolling_window( - input, 2, 1, 1, *make_count_aggregation(null_policy::INCLUDE)); - auto const rowno = - rolling_window(input, 2, 1, 1, *make_row_number_aggregation()); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sum, sum->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, min->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, max->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, lag->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, lead->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, valid->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, all->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno, rowno->view()); -} - -class RollingDictionaryTest : public cudf::test::BaseFixture { -}; - -TEST_F(RollingDictionaryTest, Count) -{ - cudf::test::dictionary_column_wrapper input( - {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, - {1, 0, 0, 1, 0, 1, 1, 1, 0}); - fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 1}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - fixed_width_column_wrapper expected_row_number({1, 2, 2, 2, 2, 2, 2, 2, 2}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - - auto got_count_valid = cudf::rolling_window( - input, 2, 2, 1, *cudf::make_count_aggregation()); - auto got_count_all = cudf::rolling_window( - input, - 2, - 2, - 1, - *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); - auto got_row_number = cudf::rolling_window( - input, 2, 2, 1, *cudf::make_row_number_aggregation()); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_row_number, got_row_number->view()); -} - -TEST_F(RollingDictionaryTest, MinMax) -{ - cudf::test::dictionary_column_wrapper input( - {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, - {1, 0, 0, 1, 0, 1, 1, 1, 0}); - cudf::test::strings_column_wrapper expected_min( - {"This", "This", "test", "operated", "on", "on", "on", "on", "string"}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - cudf::test::strings_column_wrapper expected_max( - {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, - {1, 1, 1, 1, 1, 1, 1, 1, 1}); - - auto got_min_dict = - cudf::rolling_window(input, 2, 2, 1, *cudf::make_min_aggregation()); - auto got_min = cudf::dictionary::decode(cudf::dictionary_column_view(got_min_dict->view())); - - auto got_max_dict = - cudf::rolling_window(input, 2, 2, 1, *cudf::make_max_aggregation()); - auto got_max = cudf::dictionary::decode(cudf::dictionary_column_view(got_max_dict->view())); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); -} - -TEST_F(RollingDictionaryTest, LeadLag) -{ - cudf::test::dictionary_column_wrapper input( - {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, - {1, 0, 0, 1, 0, 1, 1, 1, 0}); - cudf::test::strings_column_wrapper expected_lead( - {"", "", "test", "", "operated", "on", "string", "", ""}, {0, 0, 1, 0, 1, 1, 1, 0, 0}); - cudf::test::strings_column_wrapper expected_lag( - {"", "This", "", "", "test", "", "operated", "on", "string"}, {0, 1, 0, 0, 1, 0, 1, 1, 1}); - - auto got_lead_dict = cudf::rolling_window( - input, 2, 1, 1, *cudf::make_lead_aggregation(1)); - auto got_lead = cudf::dictionary::decode(cudf::dictionary_column_view(got_lead_dict->view())); - - auto got_lag_dict = - cudf::rolling_window(input, 2, 2, 1, *cudf::make_lag_aggregation(1)); - auto got_lag = cudf::dictionary::decode(cudf::dictionary_column_view(got_lag_dict->view())); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, got_lead->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, got_lag->view()); -} +// using RollingTestStrings = RollingTest; + +// TEST_F(RollingTestStrings, StringsUnsupportedOperators) +// { +// cudf::test::strings_column_wrapper input{{"This", "is", "not", "a", "string", "type"}, +// {1, 1, 1, 0, 1, 0}}; + +// std::vector window{1}; + +// EXPECT_THROW( +// cudf::rolling_window(input, 2, 2, 0, +// *cudf::make_sum_aggregation()), cudf::logic_error); +// EXPECT_THROW( +// cudf::rolling_window(input, 2, 2, 0, +// *cudf::make_mean_aggregation()), cudf::logic_error); +// EXPECT_THROW(cudf::rolling_window(input, +// 2, +// 2, +// 0, +// *cudf::make_udf_aggregation( +// cudf::udf_type::PTX, std::string{}, cudf::data_type{})), +// cudf::logic_error); +// EXPECT_THROW(cudf::rolling_window(input, +// 2, +// 2, +// 0, +// *cudf::make_udf_aggregation( +// cudf::udf_type::CUDA, std::string{}, cudf::data_type{})), +// cudf::logic_error); +// } + +// /*TEST_F(RollingTestStrings, SimpleStatic) +// { +// cudf::test::strings_column_wrapper input{{"This", "is", "not", "a", "string", "type"}, +// {1, 1, 1, 0, 1, 0}}; + +// std::vector window{1}; + +// EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::MIN)); +// EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::MAX)); +// EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::COUNT_VALID)); +// EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::COUNT_ALL)); +// }*/ + +// struct RollingTestUdf : public cudf::test::BaseFixture { +// const std::string cuda_func{ +// R"***( +// template +// __device__ void CUDA_GENERIC_AGGREGATOR(OutType *ret, InType *in_col, cudf::size_type +// start, +// cudf::size_type count) { +// OutType val = 0; +// for (cudf::size_type i = 0; i < count; i++) { +// val += in_col[start + i]; +// } +// *ret = val; +// } +// )***"}; + +// const std::string ptx_func{ +// R"***( +// // +// // Generated by NVIDIA NVVM Compiler +// // +// // Compiler Build ID: CL-24817639 +// // Cuda compilation tools, release 10.0, V10.0.130 +// // Based on LLVM 3.4svn +// // + +// .version 6.3 +// .target sm_70 +// .address_size 64 + +// // .globl _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE +// .common .global .align 8 .u64 _ZN08NumbaEnv8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE; + +// .visible .func (.param .b32 func_retval0) +// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE( .param .b64 +// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0, .param .b64 +// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_1, .param .b64 +// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_2, .param .b64 +// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_3, .param .b64 +// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_4, .param .b64 +// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5, .param .b64 +// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6, .param .b64 +// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_7 +// ) +// { +// .reg .pred %p<3>; +// .reg .b32 %r<6>; +// .reg .b64 %rd<18>; + +// ld.param.u64 %rd6, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0]; +// ld.param.u64 %rd7, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5]; +// ld.param.u64 %rd8, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6]; +// ld.param.u64 %rd9, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_7]; +// mov.u64 %rd15, 0; +// mov.u64 %rd16, %rd15; + +// BB0_1: +// mov.u64 %rd2, %rd16; +// mov.u32 %r5, 0; +// setp.ge.s64 %p1, %rd15, %rd8; +// mov.u64 %rd17, %rd15; +// @%p1 bra BB0_3; + +// mul.lo.s64 %rd12, %rd15, %rd9; +// add.s64 %rd13, %rd12, %rd7; +// ld.u32 %r5, [%rd13]; +// add.s64 %rd17, %rd15, 1; + +// BB0_3: +// cvt.s64.s32 %rd14, %r5; +// add.s64 %rd16, %rd14, %rd2; +// setp.lt.s64 %p2, %rd15, %rd8; +// mov.u64 %rd15, %rd17; +// @%p2 bra BB0_1; + +// st.u64 [%rd6], %rd2; +// mov.u32 %r4, 0; +// st.param.b32 [func_retval0+0], %r4; +// ret; +// } +// )***"}; +// }; + +// TEST_F(RollingTestUdf, StaticWindow) +// { +// size_type size = 1000; + +// fixed_width_column_wrapper input(thrust::make_counting_iterator(0), +// thrust::make_counting_iterator(size), +// thrust::make_constant_iterator(true)); + +// std::unique_ptr output; + +// auto start = cudf::detail::make_counting_transform_iterator(0, [size](size_type row) { +// return std::accumulate(thrust::make_counting_iterator(std::max(0, row - 2 + 1)), +// thrust::make_counting_iterator(std::min(size, row + 2 + 1)), +// 0); +// }); + +// auto valid = cudf::detail::make_counting_transform_iterator( +// 0, [size](size_type row) { return (row != 0 && row != size - 2 && row != size - 1); }); + +// fixed_width_column_wrapper expected{start, start + size, valid}; + +// // Test CUDA UDF +// auto cuda_udf_agg = cudf::make_udf_aggregation( +// cudf::udf_type::CUDA, this->cuda_func, cudf::data_type{cudf::type_id::INT64}); + +// output = cudf::rolling_window(input, 2, 2, 4, *cuda_udf_agg); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); + +// // Test NUMBA UDF +// auto ptx_udf_agg = cudf::make_udf_aggregation( +// cudf::udf_type::PTX, this->ptx_func, cudf::data_type{cudf::type_id::INT64}); + +// output = cudf::rolling_window(input, 2, 2, 4, *ptx_udf_agg); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); +// } + +// TEST_F(RollingTestUdf, DynamicWindow) +// { +// size_type size = 1000; + +// fixed_width_column_wrapper input(thrust::make_counting_iterator(0), +// thrust::make_counting_iterator(size), +// thrust::make_constant_iterator(true)); + +// auto prec = cudf::detail::make_counting_transform_iterator( +// 0, [size] __device__(size_type row) { return row % 2 + 2; }); + +// auto follow = cudf::detail::make_counting_transform_iterator( +// 0, [size] __device__(size_type row) { return row % 2; }); + +// fixed_width_column_wrapper preceding(prec, prec + size); +// fixed_width_column_wrapper following(follow, follow + size); +// std::unique_ptr output; + +// auto start = cudf::detail::make_counting_transform_iterator(0, [size] __device__(size_type row) +// { +// return std::accumulate(thrust::make_counting_iterator(std::max(0, row - (row % 2 + 2) + 1)), +// thrust::make_counting_iterator(std::min(size, row + (row % 2) + 1)), +// 0); +// }); + +// auto valid = cudf::detail::make_counting_transform_iterator( +// 0, [size] __device__(size_type row) { return row != 0; }); + +// fixed_width_column_wrapper expected{start, start + size, valid}; + +// // Test CUDA UDF +// auto cuda_udf_agg = cudf::make_udf_aggregation( +// cudf::udf_type::CUDA, this->cuda_func, cudf::data_type{cudf::type_id::INT64}); + +// output = cudf::rolling_window(input, preceding, following, 2, *cuda_udf_agg); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); + +// // Test PTX UDF +// auto ptx_udf_agg = cudf::make_udf_aggregation( +// cudf::udf_type::PTX, this->ptx_func, cudf::data_type{cudf::type_id::INT64}); + +// output = cudf::rolling_window(input, preceding, following, 2, *ptx_udf_agg); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); +// } + +// template +// struct FixedPointTests : public cudf::test::BaseFixture { +// }; + +// TYPED_TEST_CASE(FixedPointTests, cudf::test::FixedPointTypes); + +// TYPED_TEST(FixedPointTests, MinMaxCountLagLead) +// { +// using namespace numeric; +// using namespace cudf; +// using decimalXX = TypeParam; +// using RepType = cudf::device_storage_type_t; +// using fp_wrapper = cudf::test::fixed_point_column_wrapper; +// using fw_wrapper = cudf::test::fixed_width_column_wrapper; + +// auto const scale = scale_type{-1}; +// auto const input = fp_wrapper{{42, 1729, 55, 3, 1, 2}, {1, 1, 1, 1, 1, 1}, scale}; +// auto const expected_min = fp_wrapper{{42, 42, 3, 1, 1, 1}, {1, 1, 1, 1, 1, 1}, scale}; +// auto const expected_max = fp_wrapper{{1729, 1729, 1729, 55, 3, 2}, {1, 1, 1, 1, 1, 1}, scale}; +// auto const expected_lag = fp_wrapper{{0, 42, 1729, 55, 3, 1}, {0, 1, 1, 1, 1, 1}, scale}; +// auto const expected_lead = fp_wrapper{{1729, 55, 3, 1, 2, 0}, {1, 1, 1, 1, 1, 0}, scale}; +// auto const expected_count_val = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; +// auto const expected_count_all = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; +// auto const expected_rowno = fw_wrapper{{1, 2, 2, 2, 2, 2}, {1, 1, 1, 1, 1, 1}}; +// auto const expected_rowno1 = fw_wrapper{{1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1}}; + +// auto const min = +// rolling_window(input, 2, 1, 1, *make_min_aggregation()); +// auto const max = +// rolling_window(input, 2, 1, 1, *make_max_aggregation()); +// auto const lag = +// rolling_window(input, 2, 1, 1, *make_lag_aggregation(1)); +// auto const lead = +// rolling_window(input, 2, 1, 1, *make_lead_aggregation(1)); +// auto const valid = +// rolling_window(input, 2, 1, 1, *make_count_aggregation()); +// auto const all = rolling_window( +// input, 2, 1, 1, *make_count_aggregation(null_policy::INCLUDE)); +// auto const rowno = +// rolling_window(input, 2, 1, 1, *make_row_number_aggregation()); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, min->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, max->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, lag->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, lead->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, valid->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, all->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno, rowno->view()); + +// // ROW_NUMBER will always return row 1 if the preceding window is set to a constant 1 +// for (int following = 1; following < 5; ++following) { +// auto const rowno1 = rolling_window( +// input, 1, following, 1, *make_row_number_aggregation()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno1, rowno1->view()); +// } +// } + +// TYPED_TEST(FixedPointTests, MinMaxCountLagLeadNulls) +// { +// using namespace numeric; +// using namespace cudf; +// using decimalXX = TypeParam; +// using RepType = cudf::device_storage_type_t; +// using fp_wrapper = cudf::test::fixed_point_column_wrapper; +// using fp64_wrapper = cudf::test::fixed_point_column_wrapper; +// using fw_wrapper = cudf::test::fixed_width_column_wrapper; + +// auto const scale = scale_type{-1}; +// auto const input = fp_wrapper{{42, 1729, 55, 343, 1, 2}, {1, 0, 1, 0, 1, 1}, +// scale}; auto const expected_sum = fp64_wrapper{{42, 97, 55, 56, 3, 3}, {1, 1, 1, 1, 1, +// 1}, scale}; auto const expected_min = fp_wrapper{{42, 42, 55, 1, 1, 1}, {1, 1, 1, 1, 1, +// 1}, scale}; auto const expected_max = fp_wrapper{{42, 55, 55, 55, 2, 2}, {1, 1, 1, 1, 1, +// 1}, scale}; auto const expected_lag = fp_wrapper{{0, 42, 1729, 55, 343, 1}, {0, 1, 0, 1, +// 0, 1}, scale}; auto const expected_lead = fp_wrapper{{1729, 55, 343, 1, 2, 0}, {0, 1, 0, +// 1, 1, 0}, scale}; auto const expected_count_val = fw_wrapper{{1, 2, 1, 2, 2, 2}, {1, 1, 1, 1, +// 1, 1}}; auto const expected_count_all = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; +// auto const expected_rowno = fw_wrapper{{1, 2, 2, 2, 2, 2}, {1, 1, 1, 1, 1, 1}}; + +// auto const sum = +// rolling_window(input, 2, 1, 1, *make_sum_aggregation()); +// auto const min = +// rolling_window(input, 2, 1, 1, *make_min_aggregation()); +// auto const max = +// rolling_window(input, 2, 1, 1, *make_max_aggregation()); +// auto const lag = +// rolling_window(input, 2, 1, 1, *make_lag_aggregation(1)); +// auto const lead = +// rolling_window(input, 2, 1, 1, *make_lead_aggregation(1)); +// auto const valid = +// rolling_window(input, 2, 1, 1, *make_count_aggregation()); +// auto const all = rolling_window( +// input, 2, 1, 1, *make_count_aggregation(null_policy::INCLUDE)); +// auto const rowno = +// rolling_window(input, 2, 1, 1, *make_row_number_aggregation()); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sum, sum->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, min->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, max->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, lag->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, lead->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, valid->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, all->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno, rowno->view()); +// } + +// class RollingDictionaryTest : public cudf::test::BaseFixture { +// }; + +// TEST_F(RollingDictionaryTest, Count) +// { +// cudf::test::dictionary_column_wrapper input( +// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, +// {1, 0, 0, 1, 0, 1, 1, 1, 0}); +// fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 1}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); +// fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); +// fixed_width_column_wrapper expected_row_number({1, 2, 2, 2, 2, 2, 2, 2, 2}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); + +// auto got_count_valid = cudf::rolling_window( +// input, 2, 2, 1, *cudf::make_count_aggregation()); +// auto got_count_all = cudf::rolling_window( +// input, +// 2, +// 2, +// 1, +// *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); +// auto got_row_number = cudf::rolling_window( +// input, 2, 2, 1, *cudf::make_row_number_aggregation()); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_row_number, got_row_number->view()); +// } + +// TEST_F(RollingDictionaryTest, MinMax) +// { +// cudf::test::dictionary_column_wrapper input( +// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, +// {1, 0, 0, 1, 0, 1, 1, 1, 0}); +// cudf::test::strings_column_wrapper expected_min( +// {"This", "This", "test", "operated", "on", "on", "on", "on", "string"}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); +// cudf::test::strings_column_wrapper expected_max( +// {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, +// {1, 1, 1, 1, 1, 1, 1, 1, 1}); + +// auto got_min_dict = +// cudf::rolling_window(input, 2, 2, 1, +// *cudf::make_min_aggregation()); +// auto got_min = cudf::dictionary::decode(cudf::dictionary_column_view(got_min_dict->view())); + +// auto got_max_dict = +// cudf::rolling_window(input, 2, 2, 1, +// *cudf::make_max_aggregation()); +// auto got_max = cudf::dictionary::decode(cudf::dictionary_column_view(got_max_dict->view())); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); +// } + +// TEST_F(RollingDictionaryTest, LeadLag) +// { +// cudf::test::dictionary_column_wrapper input( +// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, +// {1, 0, 0, 1, 0, 1, 1, 1, 0}); +// cudf::test::strings_column_wrapper expected_lead( +// {"", "", "test", "", "operated", "on", "string", "", ""}, {0, 0, 1, 0, 1, 1, 1, 0, 0}); +// cudf::test::strings_column_wrapper expected_lag( +// {"", "This", "", "", "test", "", "operated", "on", "string"}, {0, 1, 0, 0, 1, 0, 1, 1, 1}); + +// auto got_lead_dict = cudf::rolling_window( +// input, 2, 1, 1, *cudf::make_lead_aggregation(1)); +// auto got_lead = cudf::dictionary::decode(cudf::dictionary_column_view(got_lead_dict->view())); + +// auto got_lag_dict = +// cudf::rolling_window(input, 2, 2, 1, +// *cudf::make_lag_aggregation(1)); +// auto got_lag = cudf::dictionary::decode(cudf::dictionary_column_view(got_lag_dict->view())); + +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, got_lead->view()); +// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, got_lag->view()); +// } CUDF_TEST_PROGRAM_MAIN() From 2f63568db7763bee0c3898146a1a8d917caef488 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 22 Jul 2021 21:15:22 -0700 Subject: [PATCH 04/47] clean up --- cpp/src/rolling/rolling_detail.cuh | 140 +- cpp/tests/rolling/rolling_test.cpp | 1988 ++++++++++++++-------------- 2 files changed, 1067 insertions(+), 1061 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 27647fa82ea..11aac0a1659 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -17,6 +17,7 @@ #pragma once #include "cudf/detail/null_mask.hpp" +#include "cudf/detail/unary.hpp" #include "cudf/utilities/type_dispatcher.hpp" #include "lead_lag_nested_detail.cuh" #include "rmm/exec_policy.hpp" @@ -24,6 +25,7 @@ #include "rolling/rolling_detail.hpp" #include "rolling/rolling_jit_detail.hpp" #include "rolling_detail.hpp" +#include "thrust/pair.h" #include #include @@ -67,6 +69,8 @@ #include +#undef DEBUG + namespace cudf { namespace detail { @@ -314,6 +318,7 @@ struct DeviceRollingVariance { cudf::size_type count{0}; // valid counts in the window + // Count valid observations in window if (has_nulls) { count = thrust::count_if(thrust::seq, thrust::make_counting_iterator(start_index), @@ -323,39 +328,71 @@ struct DeviceRollingVariance { count = end_index - start_index; } - // Variance/Std is non-negative and thus ddof should be strictly less than valid counts. - bool output_is_valid = (count >= min_periods) and not(count <= ddof); + // Variance/Std is non-negative, thus ddof should be strictly less than valid counts. + // Variance/Std of a lone value is statistically meaningless + bool output_is_valid = (count >= min_periods) and not(count == 1) and not(count <= ddof); +#ifdef DEBUG + printf("count: %d\n", count); +#endif if (output_is_valid) { - OutputType mean = thrust::reduce(thrust::seq, - thrust::make_counting_iterator(start_index), - thrust::make_counting_iterator(end_index), - OutputType{0}, - [&](auto acc, auto i) { - if (has_nulls) { - return input.is_valid_nocheck(i) - ? acc + input.element(i) - : acc; - } else { - return acc + input.element(i); - } - }) / - count; - - output.element(current_index) = + // OutputType mean = thrust::reduce(thrust::seq, + // thrust::make_counting_iterator(start_index), + // thrust::make_counting_iterator(end_index), + // OutputType{0}, + // [&](auto acc, auto i) { + // if (has_nulls) { + // return input.is_valid_nocheck(i) + // ? acc + input.element(i) + // : acc; + // } else { + // return acc + input.element(i); + // } + // }) / + // count; + + // output.element(current_index) = + // thrust::reduce(thrust::seq, + // thrust::make_counting_iterator(start_index), + // thrust::make_counting_iterator(end_index), + // OutputType{0}, + // [&](auto acc, auto i) { + // auto x = input.element(i); + // if (has_nulls) { + // return input.is_valid_nocheck(i) ? acc + (x - mean) * (x - mean) : + // acc; + // } else { + // return acc + (x - mean) * (x - mean); + // } + // }) / + // (count - ddof); + + // Welford algorithm + OutputType m, m2; + size_type running_count; + + thrust::tie(running_count, m, m2) = thrust::reduce(thrust::seq, thrust::make_counting_iterator(start_index), thrust::make_counting_iterator(end_index), - OutputType{0}, + thrust::make_tuple(size_type{0}, OutputType{0}, OutputType{0}), [&](auto acc, auto i) { - auto x = input.element(i); - if (has_nulls) { - return input.is_valid_nocheck(i) ? acc + (x - mean) * (x - mean) : acc; - } else { - return acc + (x - mean) * (x - mean); - } - }) / - (count - ddof); + if (has_nulls and input.is_null_nocheck(i)) { return acc; } + + OutputType m_acc, m2_acc, tmp1, tmp2; + size_type r_count_acc; + thrust::tie(r_count_acc, m_acc, m2_acc) = acc; + OutputType x = static_cast(input.element(i)); + + r_count_acc++; + tmp1 = x - m_acc; + m_acc += tmp1 / r_count_acc; + tmp2 = x - m_acc; + m2_acc += tmp1 * tmp2; + return thrust::make_tuple(r_count_acc, m_acc, m2_acc); + }); + + output.element(current_index) = m2 / (count - ddof); } return output_is_valid; @@ -594,11 +631,6 @@ struct corresponding_rolling_operator { using type = DeviceRollingVariance; }; -template -struct corresponding_rolling_operator { - using type = DeviceRollingVariance; -}; - template struct corresponding_rolling_operator { using type = DeviceRollingLag; @@ -620,20 +652,18 @@ struct create_rolling_operator< InputType, op, std::enable_if_t::type::is_supported()>> { - template < - typename T = InputType, - aggregation::Kind O = op, - std::enable_if_t* = nullptr> + template * = nullptr> auto operator()(size_type min_periods, rolling_aggregation const&) { return typename corresponding_rolling_operator::type(min_periods); } - template < - typename T = InputType, - aggregation::Kind O = op, - std::enable_if_t* = nullptr> + template * = nullptr> auto operator()(size_type min_periods, rolling_aggregation const& agg) { return DeviceRollingVariance{ @@ -736,6 +766,16 @@ class rolling_aggregation_preprocessor final : public cudf::detail::simple_aggre return {}; } + // STD aggregations depends on VARIANCE aggregation. Each element is applied + // with sqaured-root in the finalize() step. + std::vector> visit(data_type, + cudf::detail::std_aggregation const& agg) override + { + std::vector> aggs; + aggs.push_back(make_variance_aggregation(agg._ddof)); + return aggs; + } + // LEAD and LAG have custom behaviors for non fixed-width types. std::vector> visit( data_type col_type, cudf::detail::lead_lag_aggregation const& agg) override @@ -857,18 +897,7 @@ class rolling_aggregation_postprocessor final : public cudf::detail::aggregation // perform the element-wise square root operation on result of VARIANCE void visit(cudf::detail::std_aggregation const& agg) override { - result = make_numeric_column( - data_type{type_to_id()}, intermediate->size(), mask_state::UNALLOCATED, stream, mr); - column_view intermediate_view = intermediate->view(); - mutable_column_view result_mview = result->mutable_view(); - thrust::transform(rmm::exec_policy(stream), - intermediate_view.begin(), - intermediate_view.end(), - result_mview.begin(), - [] __device__(auto v) { return std::sqrt(v); }); - - result->set_null_mask(detail::copy_bitmask(intermediate_view, stream, mr), - intermediate_view.null_count()); + result = detail::unary_operation(intermediate->view(), unary_operator::SQRT, stream, mr); } std::unique_ptr get_result() @@ -1111,9 +1140,6 @@ struct dispatch_rolling { rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) { - if constexpr (is_fixed_width() and not is_chrono()) - cudf::debug::print>(input, std::cout, ",", stream.value()); - // do any preprocessing of aggregations (eg, MIN -> ARGMIN, COLLECT_LIST -> nothing) rolling_aggregation_preprocessor preprocessor; auto preprocessed_aggs = agg.get_simple_aggregations(input.type(), preprocessor); @@ -1135,8 +1161,6 @@ struct dispatch_rolling { mr) : nullptr; - cudf::debug::print(intermediate->view(), std::cout, ",", stream.value()); - // finalize. auto const result_type = target_type(input.type(), agg.kind); rolling_aggregation_postprocessor postprocessor(input, diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 33cccbfc302..55eb87208b7 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -41,127 +41,127 @@ using cudf::bitmask_type; using cudf::size_type; using cudf::test::fixed_width_column_wrapper; -// class RollingStringTest : public cudf::test::BaseFixture { -// }; +class RollingStringTest : public cudf::test::BaseFixture { +}; -// TEST_F(RollingStringTest, NoNullStringMinMaxCount) -// { -// cudf::test::strings_column_wrapper input( -// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}); -// std::vector window{2}; -// cudf::test::strings_column_wrapper expected_min( -// {"This", "This", "being", "being", "being", "being", "column", "column", "column"}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); -// cudf::test::strings_column_wrapper expected_max( -// {"rolling", "test", "test", "test", "test", "string", "string", "string", "string"}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); -// fixed_width_column_wrapper expected_count({3, 4, 4, 4, 4, 4, 4, 3, 2}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); - -// auto got_min = cudf::rolling_window( -// input, window[0], window[0], 1, *cudf::make_min_aggregation()); -// auto got_max = cudf::rolling_window( -// input, window[0], window[0], 1, *cudf::make_max_aggregation()); -// auto got_count_valid = cudf::rolling_window( -// input, window[0], window[0], 1, *cudf::make_count_aggregation()); -// auto got_count_all = cudf::rolling_window( -// input, -// window[0], -// window[0], -// 1, -// *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); - -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count_valid->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count_all->view()); -// } +TEST_F(RollingStringTest, NoNullStringMinMaxCount) +{ + cudf::test::strings_column_wrapper input( + {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}); + std::vector window{2}; + cudf::test::strings_column_wrapper expected_min( + {"This", "This", "being", "being", "being", "being", "column", "column", "column"}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + cudf::test::strings_column_wrapper expected_max( + {"rolling", "test", "test", "test", "test", "string", "string", "string", "string"}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + fixed_width_column_wrapper expected_count({3, 4, 4, 4, 4, 4, 4, 3, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + + auto got_min = cudf::rolling_window( + input, window[0], window[0], 1, *cudf::make_min_aggregation()); + auto got_max = cudf::rolling_window( + input, window[0], window[0], 1, *cudf::make_max_aggregation()); + auto got_count_valid = cudf::rolling_window( + input, window[0], window[0], 1, *cudf::make_count_aggregation()); + auto got_count_all = cudf::rolling_window( + input, + window[0], + window[0], + 1, + *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count_valid->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count_all->view()); +} -// TEST_F(RollingStringTest, NullStringMinMaxCount) -// { -// cudf::test::strings_column_wrapper input( -// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, -// {1, 0, 0, 1, 0, 1, 1, 1, 0}); -// std::vector window{2}; -// cudf::test::strings_column_wrapper expected_min( -// {"This", "This", "test", "operated", "on", "on", "on", "on", "string"}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); -// cudf::test::strings_column_wrapper expected_max( -// {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); -// fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 1}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); -// fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); - -// auto got_min = cudf::rolling_window( -// input, window[0], window[0], 1, *cudf::make_min_aggregation()); -// auto got_max = cudf::rolling_window( -// input, window[0], window[0], 1, *cudf::make_max_aggregation()); -// auto got_count_valid = cudf::rolling_window( -// input, window[0], window[0], 1, *cudf::make_count_aggregation()); -// auto got_count_all = cudf::rolling_window( -// input, -// window[0], -// window[0], -// 1, -// *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); - -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); -// } +TEST_F(RollingStringTest, NullStringMinMaxCount) +{ + cudf::test::strings_column_wrapper input( + {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, + {1, 0, 0, 1, 0, 1, 1, 1, 0}); + std::vector window{2}; + cudf::test::strings_column_wrapper expected_min( + {"This", "This", "test", "operated", "on", "on", "on", "on", "string"}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + cudf::test::strings_column_wrapper expected_max( + {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + + auto got_min = cudf::rolling_window( + input, window[0], window[0], 1, *cudf::make_min_aggregation()); + auto got_max = cudf::rolling_window( + input, window[0], window[0], 1, *cudf::make_max_aggregation()); + auto got_count_valid = cudf::rolling_window( + input, window[0], window[0], 1, *cudf::make_count_aggregation()); + auto got_count_all = cudf::rolling_window( + input, + window[0], + window[0], + 1, + *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); +} -// TEST_F(RollingStringTest, MinPeriods) -// { -// cudf::test::strings_column_wrapper input( -// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, -// {1, 0, 0, 1, 0, 1, 1, 1, 0}); -// std::vector window{2}; -// cudf::test::strings_column_wrapper expected_min( -// {"This", "This", "This", "operated", "on", "on", "on", "on", "on"}, -// {0, 0, 0, 0, 1, 1, 1, 0, 0}); -// cudf::test::strings_column_wrapper expected_max( -// {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, -// {0, 0, 0, 0, 1, 1, 1, 0, 0}); -// fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 2}, -// {1, 1, 1, 1, 1, 1, 1, 1, 0}); -// fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, -// {0, 1, 1, 1, 1, 1, 1, 0, 0}); - -// auto got_min = cudf::rolling_window( -// input, window[0], window[0], 3, *cudf::make_min_aggregation()); -// auto got_max = cudf::rolling_window( -// input, window[0], window[0], 3, *cudf::make_max_aggregation()); -// auto got_count_valid = cudf::rolling_window( -// input, window[0], window[0], 3, *cudf::make_count_aggregation()); -// auto got_count_all = cudf::rolling_window( -// input, -// window[0], -// window[0], -// 4, -// *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); - -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); -// } +TEST_F(RollingStringTest, MinPeriods) +{ + cudf::test::strings_column_wrapper input( + {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, + {1, 0, 0, 1, 0, 1, 1, 1, 0}); + std::vector window{2}; + cudf::test::strings_column_wrapper expected_min( + {"This", "This", "This", "operated", "on", "on", "on", "on", "on"}, + {0, 0, 0, 0, 1, 1, 1, 0, 0}); + cudf::test::strings_column_wrapper expected_max( + {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, + {0, 0, 0, 0, 1, 1, 1, 0, 0}); + fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 0}); + fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, + {0, 1, 1, 1, 1, 1, 1, 0, 0}); + + auto got_min = cudf::rolling_window( + input, window[0], window[0], 3, *cudf::make_min_aggregation()); + auto got_max = cudf::rolling_window( + input, window[0], window[0], 3, *cudf::make_max_aggregation()); + auto got_count_valid = cudf::rolling_window( + input, window[0], window[0], 3, *cudf::make_count_aggregation()); + auto got_count_all = cudf::rolling_window( + input, + window[0], + window[0], + 4, + *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); +} -// TEST_F(RollingStringTest, ZeroWindowSize) -// { -// cudf::test::strings_column_wrapper input( -// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, -// {1, 0, 0, 1, 0, 1, 1, 1, 0}); -// fixed_width_column_wrapper expected_count({0, 0, 0, 0, 0, 0, 0, 0, 0}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); +TEST_F(RollingStringTest, ZeroWindowSize) +{ + cudf::test::strings_column_wrapper input( + {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, + {1, 0, 0, 1, 0, 1, 1, 1, 0}); + fixed_width_column_wrapper expected_count({0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); -// auto got_count = cudf::rolling_window( -// input, 0, 0, 0, *cudf::make_count_aggregation()); + auto got_count = cudf::rolling_window( + input, 0, 0, 0, *cudf::make_count_aggregation()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count->view()); -// } + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count, got_count->view()); +} template class RollingTest : public cudf::test::BaseFixture { @@ -206,7 +206,11 @@ class RollingTest : public cudf::test::BaseFixture { std::cout << "\n"; #endif - CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, *reference); + if (op.kind == cudf::aggregation::VARIANCE or op.kind == cudf::aggregation::STD) { + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*output, *reference); + } else { + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, *reference); + } } // helper function to test all aggregators @@ -216,53 +220,52 @@ class RollingTest : public cudf::test::BaseFixture { size_type min_periods) { // test all supported aggregators - // run_test_col(input, - // preceding_window, - // following_window, - // min_periods, - // *cudf::make_min_aggregation()); - // run_test_col(input, - // preceding_window, - // following_window, - // min_periods, - // *cudf::make_count_aggregation()); - // run_test_col( - // input, - // preceding_window, - // following_window, - // min_periods, - // *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); - // run_test_col(input, - // preceding_window, - // following_window, - // min_periods, - // *cudf::make_max_aggregation()); - - // if (not cudf::is_timestamp(input.type())) { - // run_test_col(input, - // preceding_window, - // following_window, - // min_periods, - // *cudf::make_sum_aggregation()); - // run_test_col(input, - // preceding_window, - // following_window, - // min_periods, - // *cudf::make_mean_aggregation()); - // } + run_test_col(input, + preceding_window, + following_window, + min_periods, + *cudf::make_min_aggregation()); + run_test_col(input, + preceding_window, + following_window, + min_periods, + *cudf::make_count_aggregation()); + run_test_col( + input, + preceding_window, + following_window, + min_periods, + *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); + run_test_col(input, + preceding_window, + following_window, + min_periods, + *cudf::make_max_aggregation()); + + if (not cudf::is_timestamp(input.type())) { + run_test_col(input, + preceding_window, + following_window, + min_periods, + *cudf::make_sum_aggregation()); + run_test_col(input, + preceding_window, + following_window, + min_periods, + *cudf::make_mean_aggregation()); + } if (cudf::is_fixed_width(input.type()) and not cudf::is_chrono(input.type())) { - std::cout << "Running variance test\n"; run_test_col(input, preceding_window, following_window, min_periods, *cudf::make_variance_aggregation()); - // run_test_col(input, - // preceding_window, - // following_window, - // min_periods, - // *cudf::make_std_aggregation()); + run_test_col(input, + preceding_window, + following_window, + min_periods, + *cudf::make_std_aggregation()); } } @@ -323,16 +326,6 @@ class RollingTest : public cudf::test::BaseFixture { size_type min_periods, size_type ddof) { - std::cout << "ddof " << ddof << std::endl; - std::cout << "preceding_window_col:\n"; - for (auto x : preceding_window_col) { - std::cout << x; - } - std::cout << "\nfollowing_window_col:\n"; - for (auto x : following_window_col) { - std::cout << x; - } - std::cout << "\n"; using OutputType = double; size_type num_rows = input.size(); @@ -357,34 +350,33 @@ class RollingTest : public cudf::test::BaseFixture { size_type start_index = std::min(start, end); size_type end_index = std::max(start, end); - // compute window var/std + // compute window var/std - with raw loop, alternative to implementation size_type count{0}; // valid count in window - OutputType mean{0}, result{0}; for (auto j = start_index; j < end_index; j++) { if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { count++; } } - if (count >= min_periods and not(count == ddof)) { - for (auto j = start_index; j < end_index; j++) { - if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { mean += in_col[j]; } - } - mean /= count; + ref_valid[i] = (count >= min_periods) and not(count == 1) and not(count == ddof); + + if (ref_valid[i]) { + OutputType mean{0}, m2{0}, tmp1, tmp2; + size_type r_count{0}; + for (auto j = start_index; j < end_index; j++) { if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { - result += (in_col[j] - mean) * (in_col[j] - mean); + r_count++; + tmp1 = in_col[j] - mean; + mean += tmp1 / r_count; + tmp2 = in_col[j] - mean; + m2 += tmp1 * tmp2; } } - result /= (end_index - start_index - ddof); - if (do_square_root) { result = std::sqrt(result); } - } - ref_data[i] = result; - ref_valid[i] = (count >= min_periods) and not(count == ddof); - std::cout << count << " " << min_periods << " " << (count >= min_periods) - << static_cast(ref_valid[i]) << std::endl; + ref_data[i] = m2 / (count - ddof); + if (do_square_root) { ref_data[i] = std::sqrt(ref_data[i]); } + } } fixed_width_column_wrapper col(ref_data.begin(), ref_data.end(), ref_valid.begin()); - cudf::test::print(col); return col.release(); } @@ -401,75 +393,74 @@ class RollingTest : public cudf::test::BaseFixture { CUDF_FAIL("Unsupported combination of type and aggregation"); } - // template ()>* = nullptr> - // std::unique_ptr create_reference_output( - // cudf::column_view const& input, - // std::vector const& preceding_window_col, - // std::vector const& following_window_col, - // size_type min_periods) - // { - // size_type num_rows = input.size(); - // thrust::host_vector ref_data(num_rows); - // thrust::host_vector ref_valid(num_rows); - - // // input data and mask - // thrust::host_vector in_col; - // std::vector in_valid; - // std::tie(in_col, in_valid) = cudf::test::to_host(input); - // bitmask_type* valid_mask = in_valid.data(); - - // agg_op op; - // for (size_type i = 0; i < num_rows; i++) { - // OutputType val = agg_op::template identity(); - - // // load sizes - // min_periods = std::max(min_periods, 1); // at least one observation is required - - // // compute bounds - // auto preceding_window = preceding_window_col[i % preceding_window_col.size()]; - // auto following_window = following_window_col[i % following_window_col.size()]; - // size_type start = std::min(num_rows, std::max(0, i - preceding_window + 1)); - // size_type end = std::min(num_rows, std::max(0, i + following_window + 1)); - // size_type start_index = std::min(start, end); - // size_type end_index = std::max(start, end); - - // // aggregate - // size_type count = 0; - // for (size_type j = start_index; j < end_index; j++) { - // if (!input.nullable() || cudf::bit_is_set(valid_mask, j)) { - // val = op(static_cast(in_col[j]), val); - // count++; - // } - // } - - // ref_valid[i] = (count >= min_periods); - // if (ref_valid[i]) { - // cudf::detail::rolling_store_output_functor{}(ref_data[i], val, - // count); - // } - // } - - // fixed_width_column_wrapper col(ref_data.begin(), ref_data.end(), - // ref_valid.begin()); return col.release(); - // } - - // template ()>* = nullptr> - // std::unique_ptr create_reference_output( - // cudf::column_view const& input, - // std::vector const& preceding_window_col, - // std::vector const& following_window_col, - // size_type min_periods) - // { - // CUDF_FAIL("Unsupported combination of type and aggregation"); - // } + template ()>* = nullptr> + std::unique_ptr create_reference_output( + cudf::column_view const& input, + std::vector const& preceding_window_col, + std::vector const& following_window_col, + size_type min_periods) + { + size_type num_rows = input.size(); + thrust::host_vector ref_data(num_rows); + thrust::host_vector ref_valid(num_rows); + + // input data and mask + thrust::host_vector in_col; + std::vector in_valid; + std::tie(in_col, in_valid) = cudf::test::to_host(input); + bitmask_type* valid_mask = in_valid.data(); + + agg_op op; + for (size_type i = 0; i < num_rows; i++) { + OutputType val = agg_op::template identity(); + + // load sizes + min_periods = std::max(min_periods, 1); // at least one observation is required + + // compute bounds + auto preceding_window = preceding_window_col[i % preceding_window_col.size()]; + auto following_window = following_window_col[i % following_window_col.size()]; + size_type start = std::min(num_rows, std::max(0, i - preceding_window + 1)); + size_type end = std::min(num_rows, std::max(0, i + following_window + 1)); + size_type start_index = std::min(start, end); + size_type end_index = std::max(start, end); + + // aggregate + size_type count = 0; + for (size_type j = start_index; j < end_index; j++) { + if (!input.nullable() || cudf::bit_is_set(valid_mask, j)) { + val = op(static_cast(in_col[j]), val); + count++; + } + } + + ref_valid[i] = (count >= min_periods); + if (ref_valid[i]) { + cudf::detail::rolling_store_output_functor{}(ref_data[i], val, count); + } + } + + fixed_width_column_wrapper col(ref_data.begin(), ref_data.end(), ref_valid.begin()); + return col.release(); + } + + template ()>* = nullptr> + std::unique_ptr create_reference_output( + cudf::column_view const& input, + std::vector const& preceding_window_col, + std::vector const& following_window_col, + size_type min_periods) + { + CUDF_FAIL("Unsupported combination of type and aggregation"); + } std::unique_ptr create_reference_output( cudf::rolling_aggregation const& op, @@ -480,36 +471,36 @@ class RollingTest : public cudf::test::BaseFixture { { // unroll aggregation types switch (op.kind) { - // case cudf::aggregation::SUM: - // return create_reference_output, - // false>( - // input, preceding_window, following_window, min_periods); - // case cudf::aggregation::MIN: - // return create_reference_output, - // false>( - // input, preceding_window, following_window, min_periods); - // case cudf::aggregation::MAX: - // return create_reference_output, - // false>( - // input, preceding_window, following_window, min_periods); - // case cudf::aggregation::COUNT_VALID: - // return create_count_reference_output( - // input, preceding_window, following_window, min_periods); - // case cudf::aggregation::COUNT_ALL: - // return create_count_reference_output( - // input, preceding_window, following_window, min_periods); - // case cudf::aggregation::MEAN: - // return create_reference_output, - // true>( - // input, preceding_window, following_window, min_periods); + case cudf::aggregation::SUM: + return create_reference_output, + false>( + input, preceding_window, following_window, min_periods); + case cudf::aggregation::MIN: + return create_reference_output, + false>( + input, preceding_window, following_window, min_periods); + case cudf::aggregation::MAX: + return create_reference_output, + false>( + input, preceding_window, following_window, min_periods); + case cudf::aggregation::COUNT_VALID: + return create_count_reference_output( + input, preceding_window, following_window, min_periods); + case cudf::aggregation::COUNT_ALL: + return create_count_reference_output( + input, preceding_window, following_window, min_periods); + case cudf::aggregation::MEAN: + return create_reference_output, + true>( + input, preceding_window, following_window, min_periods); case cudf::aggregation::VARIANCE: return create_var_std_reference_output( input, @@ -531,824 +522,815 @@ class RollingTest : public cudf::test::BaseFixture { // // ------------- expected failures -------------------- -// class RollingErrorTest : public cudf::test::BaseFixture { -// }; +class RollingErrorTest : public cudf::test::BaseFixture { +}; -// // negative sizes -// TEST_F(RollingErrorTest, NegativeMinPeriods) -// { -// const std::vector col_data = {0, 1, 2, 0, 4}; -// const std::vector col_valid = {1, 1, 1, 0, 1}; -// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), -// col_valid.begin()); - -// EXPECT_THROW( -// cudf::rolling_window(input, 2, 2, -2, -// *cudf::make_sum_aggregation()), cudf::logic_error); -// } +// negative sizes +TEST_F(RollingErrorTest, NegativeMinPeriods) +{ + const std::vector col_data = {0, 1, 2, 0, 4}; + const std::vector col_valid = {1, 1, 1, 0, 1}; + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_valid.begin()); -// // window array size mismatch -// TEST_F(RollingErrorTest, WindowArraySizeMismatch) -// { -// const std::vector col_data = {0, 1, 2, 0, 4}; -// const std::vector col_valid = {1, 1, 1, 0, 1}; -// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), -// col_valid.begin()); - -// std::vector five({2, 1, 2, 1, 4}); -// std::vector four({1, 2, 3, 4}); -// fixed_width_column_wrapper five_elements(five.begin(), five.end()); -// fixed_width_column_wrapper four_elements(four.begin(), four.end()); - -// // this runs ok -// EXPECT_NO_THROW(cudf::rolling_window(input, -// five_elements, -// five_elements, -// 1, -// *cudf::make_sum_aggregation())); - -// // mismatch for the window array -// EXPECT_THROW(cudf::rolling_window(input, -// four_elements, -// five_elements, -// 1, -// *cudf::make_sum_aggregation()), -// cudf::logic_error); - -// // mismatch for the forward window array -// EXPECT_THROW(cudf::rolling_window(input, -// five_elements, -// four_elements, -// 1, -// *cudf::make_sum_aggregation()), -// cudf::logic_error); -// } + EXPECT_THROW( + cudf::rolling_window(input, 2, 2, -2, *cudf::make_sum_aggregation()), + cudf::logic_error); +} -// TEST_F(RollingErrorTest, EmptyInput) -// { -// cudf::test::fixed_width_column_wrapper empty_col{}; -// std::unique_ptr output; -// EXPECT_NO_THROW(output = cudf::rolling_window( -// empty_col, 2, 0, 2, -// *cudf::make_sum_aggregation())); -// EXPECT_EQ(output->size(), 0); - -// fixed_width_column_wrapper preceding_window{}; -// fixed_width_column_wrapper following_window{}; -// EXPECT_NO_THROW(output = -// cudf::rolling_window(empty_col, -// preceding_window, -// following_window, -// 2, -// *cudf::make_sum_aggregation())); -// EXPECT_EQ(output->size(), 0); - -// fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; -// EXPECT_NO_THROW(output = -// cudf::rolling_window(nonempty_col, -// preceding_window, -// following_window, -// 2, -// *cudf::make_sum_aggregation())); -// EXPECT_EQ(output->size(), 0); -// } +// window array size mismatch +TEST_F(RollingErrorTest, WindowArraySizeMismatch) +{ + const std::vector col_data = {0, 1, 2, 0, 4}; + const std::vector col_valid = {1, 1, 1, 0, 1}; + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_valid.begin()); + + std::vector five({2, 1, 2, 1, 4}); + std::vector four({1, 2, 3, 4}); + fixed_width_column_wrapper five_elements(five.begin(), five.end()); + fixed_width_column_wrapper four_elements(four.begin(), four.end()); + + // this runs ok + EXPECT_NO_THROW(cudf::rolling_window(input, + five_elements, + five_elements, + 1, + *cudf::make_sum_aggregation())); + + // mismatch for the window array + EXPECT_THROW(cudf::rolling_window(input, + four_elements, + five_elements, + 1, + *cudf::make_sum_aggregation()), + cudf::logic_error); + + // mismatch for the forward window array + EXPECT_THROW(cudf::rolling_window(input, + five_elements, + four_elements, + 1, + *cudf::make_sum_aggregation()), + cudf::logic_error); +} -// TEST_F(RollingErrorTest, SizeMismatch) -// { -// fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; -// std::unique_ptr output; - -// { -// fixed_width_column_wrapper preceding_window{{1, 1}}; // wrong size -// fixed_width_column_wrapper following_window{{1, 1, 1}}; -// EXPECT_THROW( -// output = cudf::rolling_window(nonempty_col, -// preceding_window, -// following_window, -// 2, -// *cudf::make_sum_aggregation()), -// cudf::logic_error); -// } -// { -// fixed_width_column_wrapper preceding_window{{1, 1, 1}}; -// fixed_width_column_wrapper following_window{{1, 2}}; // wrong size -// EXPECT_THROW( -// output = cudf::rolling_window(nonempty_col, -// preceding_window, -// following_window, -// 2, -// *cudf::make_sum_aggregation()), -// cudf::logic_error); -// } -// } +TEST_F(RollingErrorTest, EmptyInput) +{ + cudf::test::fixed_width_column_wrapper empty_col{}; + std::unique_ptr output; + EXPECT_NO_THROW(output = cudf::rolling_window( + empty_col, 2, 0, 2, *cudf::make_sum_aggregation())); + EXPECT_EQ(output->size(), 0); + + fixed_width_column_wrapper preceding_window{}; + fixed_width_column_wrapper following_window{}; + EXPECT_NO_THROW(output = + cudf::rolling_window(empty_col, + preceding_window, + following_window, + 2, + *cudf::make_sum_aggregation())); + EXPECT_EQ(output->size(), 0); + + fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; + EXPECT_NO_THROW(output = + cudf::rolling_window(nonempty_col, + preceding_window, + following_window, + 2, + *cudf::make_sum_aggregation())); + EXPECT_EQ(output->size(), 0); +} -// TEST_F(RollingErrorTest, WindowWrongDtype) -// { -// fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; -// std::unique_ptr output; - -// fixed_width_column_wrapper preceding_window{{1.0f, 1.0f, 1.0f}}; -// fixed_width_column_wrapper following_window{{1.0f, 1.0f, 1.0f}}; -// EXPECT_THROW( -// output = cudf::rolling_window(nonempty_col, -// preceding_window, -// following_window, -// 2, -// *cudf::make_sum_aggregation()), -// cudf::logic_error); -// } +TEST_F(RollingErrorTest, SizeMismatch) +{ + fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; + std::unique_ptr output; -// // incorrect type/aggregation combo: sum of timestamps -// TEST_F(RollingErrorTest, SumTimestampNotSupported) -// { -// constexpr size_type size{10}; -// fixed_width_column_wrapper input_D( -// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); -// fixed_width_column_wrapper input_s( -// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); -// fixed_width_column_wrapper input_ms( -// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); -// fixed_width_column_wrapper input_us( -// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); -// fixed_width_column_wrapper input_ns( -// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - -// EXPECT_THROW(cudf::rolling_window( -// input_D, 2, 2, 0, *cudf::make_sum_aggregation()), -// cudf::logic_error); -// EXPECT_THROW(cudf::rolling_window( -// input_s, 2, 2, 0, *cudf::make_sum_aggregation()), -// cudf::logic_error); -// EXPECT_THROW(cudf::rolling_window( -// input_ms, 2, 2, 0, *cudf::make_sum_aggregation()), -// cudf::logic_error); -// EXPECT_THROW(cudf::rolling_window( -// input_us, 2, 2, 0, *cudf::make_sum_aggregation()), -// cudf::logic_error); -// EXPECT_THROW(cudf::rolling_window( -// input_ns, 2, 2, 0, *cudf::make_sum_aggregation()), -// cudf::logic_error); -// } + { + fixed_width_column_wrapper preceding_window{{1, 1}}; // wrong size + fixed_width_column_wrapper following_window{{1, 1, 1}}; + EXPECT_THROW( + output = cudf::rolling_window(nonempty_col, + preceding_window, + following_window, + 2, + *cudf::make_sum_aggregation()), + cudf::logic_error); + } + { + fixed_width_column_wrapper preceding_window{{1, 1, 1}}; + fixed_width_column_wrapper following_window{{1, 2}}; // wrong size + EXPECT_THROW( + output = cudf::rolling_window(nonempty_col, + preceding_window, + following_window, + 2, + *cudf::make_sum_aggregation()), + cudf::logic_error); + } +} -// // incorrect type/aggregation combo: mean of timestamps -// TEST_F(RollingErrorTest, MeanTimestampNotSupported) -// { -// constexpr size_type size{10}; -// fixed_width_column_wrapper input_D( -// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); -// fixed_width_column_wrapper input_s( -// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); -// fixed_width_column_wrapper input_ms( -// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); -// fixed_width_column_wrapper input_us( -// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); -// fixed_width_column_wrapper input_ns( -// thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); - -// EXPECT_THROW(cudf::rolling_window( -// input_D, 2, 2, 0, *cudf::make_mean_aggregation()), -// cudf::logic_error); -// EXPECT_THROW(cudf::rolling_window( -// input_s, 2, 2, 0, *cudf::make_mean_aggregation()), -// cudf::logic_error); -// EXPECT_THROW(cudf::rolling_window( -// input_ms, 2, 2, 0, *cudf::make_mean_aggregation()), -// cudf::logic_error); -// EXPECT_THROW(cudf::rolling_window( -// input_us, 2, 2, 0, *cudf::make_mean_aggregation()), -// cudf::logic_error); -// EXPECT_THROW(cudf::rolling_window( -// input_ns, 2, 2, 0, *cudf::make_mean_aggregation()), -// cudf::logic_error); -// } +TEST_F(RollingErrorTest, WindowWrongDtype) +{ + fixed_width_column_wrapper nonempty_col{{1, 2, 3}}; + std::unique_ptr output; + + fixed_width_column_wrapper preceding_window{{1.0f, 1.0f, 1.0f}}; + fixed_width_column_wrapper following_window{{1.0f, 1.0f, 1.0f}}; + EXPECT_THROW( + output = cudf::rolling_window(nonempty_col, + preceding_window, + following_window, + 2, + *cudf::make_sum_aggregation()), + cudf::logic_error); +} -TYPED_TEST_CASE(RollingTest, cudf::test::FixedWidthTypesWithoutFixedPoint); +// incorrect type/aggregation combo: sum of timestamps +TEST_F(RollingErrorTest, SumTimestampNotSupported) +{ + constexpr size_type size{10}; + fixed_width_column_wrapper input_D( + thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + fixed_width_column_wrapper input_s( + thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + fixed_width_column_wrapper input_ms( + thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + fixed_width_column_wrapper input_us( + thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + fixed_width_column_wrapper input_ns( + thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + + EXPECT_THROW(cudf::rolling_window( + input_D, 2, 2, 0, *cudf::make_sum_aggregation()), + cudf::logic_error); + EXPECT_THROW(cudf::rolling_window( + input_s, 2, 2, 0, *cudf::make_sum_aggregation()), + cudf::logic_error); + EXPECT_THROW(cudf::rolling_window( + input_ms, 2, 2, 0, *cudf::make_sum_aggregation()), + cudf::logic_error); + EXPECT_THROW(cudf::rolling_window( + input_us, 2, 2, 0, *cudf::make_sum_aggregation()), + cudf::logic_error); + EXPECT_THROW(cudf::rolling_window( + input_ns, 2, 2, 0, *cudf::make_sum_aggregation()), + cudf::logic_error); +} -// // simple example from Pandas docs -// TYPED_TEST(RollingTest, SimpleStatic) -// { -// // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html -// auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, -// 4}); const std::vector col_mask = {1, 1, 1, 0, 1}; +// incorrect type/aggregation combo: mean of timestamps +TEST_F(RollingErrorTest, MeanTimestampNotSupported) +{ + constexpr size_type size{10}; + fixed_width_column_wrapper input_D( + thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + fixed_width_column_wrapper input_s( + thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + fixed_width_column_wrapper input_ms( + thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + fixed_width_column_wrapper input_us( + thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + fixed_width_column_wrapper input_ns( + thrust::make_counting_iterator(0), thrust::make_counting_iterator(size)); + + EXPECT_THROW(cudf::rolling_window( + input_D, 2, 2, 0, *cudf::make_mean_aggregation()), + cudf::logic_error); + EXPECT_THROW(cudf::rolling_window( + input_s, 2, 2, 0, *cudf::make_mean_aggregation()), + cudf::logic_error); + EXPECT_THROW(cudf::rolling_window( + input_ms, 2, 2, 0, *cudf::make_mean_aggregation()), + cudf::logic_error); + EXPECT_THROW(cudf::rolling_window( + input_us, 2, 2, 0, *cudf::make_mean_aggregation()), + cudf::logic_error); + EXPECT_THROW(cudf::rolling_window( + input_ns, 2, 2, 0, *cudf::make_mean_aggregation()), + cudf::logic_error); +} -// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), -// col_mask.begin()); std::vector window{2}; +TYPED_TEST_CASE(RollingTest, cudf::test::FixedWidthTypesWithoutFixedPoint); -// // static sizes -// this->run_test_col_agg(input, window, window, 1); -// } +// simple example from Pandas docs +TYPED_TEST(RollingTest, SimpleStatic) +{ + // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html + auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, 4}); + const std::vector col_mask = {1, 1, 1, 0, 1}; -// // negative sizes -// TYPED_TEST(RollingTest, NegativeWindowSizes) -// { -// auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, 4}); -// auto const col_valid = std::vector{1, 1, 1, 0, 1}; -// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), -// col_valid.begin()); std::vector window{3}; std::vector -// negative_window{-2}; + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); + std::vector window{2}; -// this->run_test_col_agg(input, negative_window, window, 1); -// this->run_test_col_agg(input, window, negative_window, 1); -// this->run_test_col_agg(input, negative_window, negative_window, 1); -// } + // static sizes + this->run_test_col_agg(input, window, window, 1); +} -// // simple example from Pandas docs: -// TYPED_TEST(RollingTest, SimpleDynamic) -// { -// // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html -// auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, -// 4}); const std::vector col_mask = {1, 1, 1, 0, 1}; +// negative sizes +TYPED_TEST(RollingTest, NegativeWindowSizes) +{ + auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, 4}); + auto const col_valid = std::vector{1, 1, 1, 0, 1}; + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_valid.begin()); + std::vector window{3}; + std::vector negative_window{-2}; + + this->run_test_col_agg(input, negative_window, window, 1); + this->run_test_col_agg(input, window, negative_window, 1); + this->run_test_col_agg(input, negative_window, negative_window, 1); +} -// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), -// col_mask.begin()); std::vector preceding_window({1, 2, 3, 4, 2}); -// std::vector following_window({2, 1, 2, 1, 2}); +// simple example from Pandas docs: +TYPED_TEST(RollingTest, SimpleDynamic) +{ + // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html + auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, 4}); + const std::vector col_mask = {1, 1, 1, 0, 1}; -// // dynamic sizes -// this->run_test_col_agg(input, preceding_window, following_window, 1); -// } + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); + std::vector preceding_window({1, 2, 3, 4, 2}); + std::vector following_window({2, 1, 2, 1, 2}); -// // this is a special test to check the volatile count variable issue (see rolling.cu for detail) -// TYPED_TEST(RollingTest, VolatileCount) -// { -// auto const col_data = cudf::test::make_type_param_vector({8, 70, 45, 20, 59, 80}); -// const std::vector col_mask = {1, 1, 0, 0, 1, 0}; + // dynamic sizes + this->run_test_col_agg(input, preceding_window, following_window, 1); +} -// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), -// col_mask.begin()); std::vector preceding_window({5, 9, 4, 8, 3, 3}); -// std::vector following_window({1, 1, 9, 2, 8, 9}); +// this is a special test to check the volatile count variable issue (see rolling.cu for detail) +TYPED_TEST(RollingTest, VolatileCount) +{ + auto const col_data = cudf::test::make_type_param_vector({8, 70, 45, 20, 59, 80}); + const std::vector col_mask = {1, 1, 0, 0, 1, 0}; -// // dynamic sizes -// this->run_test_col_agg(input, preceding_window, following_window, 1); -// } + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); + std::vector preceding_window({5, 9, 4, 8, 3, 3}); + std::vector following_window({1, 1, 9, 2, 8, 9}); -// // all rows are invalid -// TYPED_TEST(RollingTest, AllInvalid) -// { -// size_type num_rows = 1000; + // dynamic sizes + this->run_test_col_agg(input, preceding_window, following_window, 1); +} -// std::vector col_data(num_rows); -// std::vector col_mask(num_rows, 0); +// all rows are invalid +TYPED_TEST(RollingTest, AllInvalid) +{ + size_type num_rows = 1000; -// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), -// col_mask.begin()); std::vector window({100}); size_type periods = 100; + std::vector col_data(num_rows); + std::vector col_mask(num_rows, 0); -// this->run_test_col_agg(input, window, window, periods); -// } + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); + std::vector window({100}); + size_type periods = 100; -// // window = following_window = 0 -// TYPED_TEST(RollingTest, ZeroWindow) -// { -// size_type num_rows = 1000; + this->run_test_col_agg(input, window, window, periods); +} -// std::vector col_data(num_rows, 1); -// std::vector col_mask(num_rows, 1); +// window = following_window = 0 +TYPED_TEST(RollingTest, ZeroWindow) +{ + size_type num_rows = 1000; -// fixed_width_column_wrapper input( -// col_data.begin(), col_data.end(), col_mask.begin()); -// std::vector window({0}); -// size_type periods = num_rows; + std::vector col_data(num_rows, 1); + std::vector col_mask(num_rows, 1); -// this->run_test_col_agg(input, window, window, periods); -// } + fixed_width_column_wrapper input( + col_data.begin(), col_data.end(), col_mask.begin()); + std::vector window({0}); + size_type periods = num_rows; -// // min_periods = 0 -// TYPED_TEST(RollingTest, ZeroPeriods) -// { -// size_type num_rows = 1000; + this->run_test_col_agg(input, window, window, periods); +} -// std::vector col_data(num_rows, 1); -// std::vector col_mask(num_rows, 1); +// min_periods = 0 +TYPED_TEST(RollingTest, ZeroPeriods) +{ + size_type num_rows = 1000; -// fixed_width_column_wrapper input( -// col_data.begin(), col_data.end(), col_mask.begin()); + std::vector col_data(num_rows, 1); + std::vector col_mask(num_rows, 1); -// std::vector window({num_rows}); -// size_type periods = 0; + fixed_width_column_wrapper input( + col_data.begin(), col_data.end(), col_mask.begin()); -// this->run_test_col_agg(input, window, window, periods); -// } + std::vector window({num_rows}); + size_type periods = 0; -// // window in one direction is not large enough to collect enough samples, -// // but if using both directions we should get == min_periods, -// // also tests out of boundary accesses -// TYPED_TEST(RollingTest, BackwardForwardWindow) -// { -// size_type num_rows = 1000; + this->run_test_col_agg(input, window, window, periods); +} -// std::vector col_data(num_rows, 1); -// std::vector col_mask(num_rows, 1); +// window in one direction is not large enough to collect enough samples, +// but if using both directions we should get == min_periods, +// also tests out of boundary accesses +TYPED_TEST(RollingTest, BackwardForwardWindow) +{ + size_type num_rows = 1000; -// fixed_width_column_wrapper input( -// col_data.begin(), col_data.end(), col_mask.begin()); + std::vector col_data(num_rows, 1); + std::vector col_mask(num_rows, 1); -// std::vector window({num_rows}); -// size_type periods = num_rows; + fixed_width_column_wrapper input( + col_data.begin(), col_data.end(), col_mask.begin()); -// this->run_test_col_agg(input, window, window, periods); -// } + std::vector window({num_rows}); + size_type periods = num_rows; -// // random input data, static parameters, no nulls -// TYPED_TEST(RollingTest, RandomStaticAllValid) -// { -// size_type num_rows = 10000; + this->run_test_col_agg(input, window, window, periods); +} -// // random input -// std::vector col_data(num_rows); -// cudf::test::UniformRandomGenerator rng; -// std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); -// fixed_width_column_wrapper input(col_data.begin(), col_data.end()); +// random input data, static parameters, no nulls +TYPED_TEST(RollingTest, RandomStaticAllValid) +{ + size_type num_rows = 10000; -// std::vector window({50}); -// size_type periods = 50; + // random input + std::vector col_data(num_rows); + cudf::test::UniformRandomGenerator rng; + std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); + fixed_width_column_wrapper input(col_data.begin(), col_data.end()); -// this->run_test_col_agg(input, window, window, periods); -// } + std::vector window({50}); + size_type periods = 50; -// // random input data, static parameters, with nulls -// TYPED_TEST(RollingTest, RandomStaticWithInvalid) -// { -// size_type num_rows = 10000; - -// // random input -// std::vector col_data(num_rows); -// std::vector col_valid(num_rows); -// cudf::test::UniformRandomGenerator rng; -// cudf::test::UniformRandomGenerator rbg; -// std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); -// std::generate(col_valid.begin(), col_valid.end(), [&rbg]() { return rbg.generate(); }); -// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), -// col_valid.begin()); + this->run_test_col_agg(input, window, window, periods); +} -// std::vector window({50}); -// size_type periods = 50; +// random input data, static parameters, with nulls +TYPED_TEST(RollingTest, RandomStaticWithInvalid) +{ + size_type num_rows = 10000; -// this->run_test_col_agg(input, window, window, periods); -// } + // random input + std::vector col_data(num_rows); + std::vector col_valid(num_rows); + cudf::test::UniformRandomGenerator rng; + cudf::test::UniformRandomGenerator rbg; + std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); + std::generate(col_valid.begin(), col_valid.end(), [&rbg]() { return rbg.generate(); }); + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_valid.begin()); -// // random input data, dynamic parameters, no nulls -// TYPED_TEST(RollingTest, RandomDynamicAllValid) -// { -// size_type num_rows = 50000; -// size_type max_window_size = 50; + std::vector window({50}); + size_type periods = 50; -// // random input -// std::vector col_data(num_rows); -// cudf::test::UniformRandomGenerator rng; -// std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); -// fixed_width_column_wrapper input(col_data.begin(), col_data.end()); + this->run_test_col_agg(input, window, window, periods); +} -// // random parameters -// cudf::test::UniformRandomGenerator window_rng(0, max_window_size); -// auto generator = [&]() { return window_rng.generate(); }; +// random input data, dynamic parameters, no nulls +TYPED_TEST(RollingTest, RandomDynamicAllValid) +{ + size_type num_rows = 50000; + size_type max_window_size = 50; -// std::vector preceding_window(num_rows); -// std::vector following_window(num_rows); + // random input + std::vector col_data(num_rows); + cudf::test::UniformRandomGenerator rng; + std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); + fixed_width_column_wrapper input(col_data.begin(), col_data.end()); -// std::generate(preceding_window.begin(), preceding_window.end(), generator); -// std::generate(following_window.begin(), following_window.end(), generator); + // random parameters + cudf::test::UniformRandomGenerator window_rng(0, max_window_size); + auto generator = [&]() { return window_rng.generate(); }; -// this->run_test_col_agg(input, preceding_window, following_window, max_window_size); -// } + std::vector preceding_window(num_rows); + std::vector following_window(num_rows); -// // random input data, dynamic parameters, with nulls -// TYPED_TEST(RollingTest, RandomDynamicWithInvalid) -// { -// size_type num_rows = 50000; -// size_type max_window_size = 50; - -// // random input with nulls -// std::vector col_data(num_rows); -// std::vector col_valid(num_rows); -// cudf::test::UniformRandomGenerator rng; -// cudf::test::UniformRandomGenerator rbg; -// std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); -// std::generate(col_valid.begin(), col_valid.end(), [&rbg]() { return rbg.generate(); }); -// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), -// col_valid.begin()); + std::generate(preceding_window.begin(), preceding_window.end(), generator); + std::generate(following_window.begin(), following_window.end(), generator); -// // random parameters -// cudf::test::UniformRandomGenerator window_rng(0, max_window_size); -// auto generator = [&]() { return window_rng.generate(); }; + this->run_test_col_agg(input, preceding_window, following_window, max_window_size); +} -// std::vector preceding_window(num_rows); -// std::vector following_window(num_rows); +// random input data, dynamic parameters, with nulls +TYPED_TEST(RollingTest, RandomDynamicWithInvalid) +{ + size_type num_rows = 50000; + size_type max_window_size = 50; -// std::generate(preceding_window.begin(), preceding_window.end(), generator); -// std::generate(following_window.begin(), following_window.end(), generator); + // random input with nulls + std::vector col_data(num_rows); + std::vector col_valid(num_rows); + cudf::test::UniformRandomGenerator rng; + cudf::test::UniformRandomGenerator rbg; + std::generate(col_data.begin(), col_data.end(), [&rng]() { return rng.generate(); }); + std::generate(col_valid.begin(), col_valid.end(), [&rbg]() { return rbg.generate(); }); + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_valid.begin()); -// this->run_test_col_agg(input, preceding_window, following_window, max_window_size); -// } + // random parameters + cudf::test::UniformRandomGenerator window_rng(0, max_window_size); + auto generator = [&]() { return window_rng.generate(); }; -TYPED_TEST(RollingTest, ContrivedVar) -{ - // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html - auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, 4}); - const std::vector col_mask = {1, 1, 1, 1, 1}; + std::vector preceding_window(num_rows); + std::vector following_window(num_rows); - fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); - std::vector pwindow{2}; - std::vector fwindow{0}; + std::generate(preceding_window.begin(), preceding_window.end(), generator); + std::generate(following_window.begin(), following_window.end(), generator); - // static sizes - this->run_test_col_agg(input, pwindow, fwindow, 2); + this->run_test_col_agg(input, preceding_window, following_window, max_window_size); } -// ------------- non-fixed-width types -------------------- +// TYPED_TEST(RollingTest, ContrivedVar) +// { +// // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html +// auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, +// 4}); const std::vector col_mask = {1, 1, 1, 1, 1}; -// using RollingTestStrings = RollingTest; +// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), +// col_mask.begin()); std::vector pwindow{2}; std::vector fwindow{0}; -// TEST_F(RollingTestStrings, StringsUnsupportedOperators) -// { -// cudf::test::strings_column_wrapper input{{"This", "is", "not", "a", "string", "type"}, -// {1, 1, 1, 0, 1, 0}}; - -// std::vector window{1}; - -// EXPECT_THROW( -// cudf::rolling_window(input, 2, 2, 0, -// *cudf::make_sum_aggregation()), cudf::logic_error); -// EXPECT_THROW( -// cudf::rolling_window(input, 2, 2, 0, -// *cudf::make_mean_aggregation()), cudf::logic_error); -// EXPECT_THROW(cudf::rolling_window(input, -// 2, -// 2, -// 0, -// *cudf::make_udf_aggregation( -// cudf::udf_type::PTX, std::string{}, cudf::data_type{})), -// cudf::logic_error); -// EXPECT_THROW(cudf::rolling_window(input, -// 2, -// 2, -// 0, -// *cudf::make_udf_aggregation( -// cudf::udf_type::CUDA, std::string{}, cudf::data_type{})), -// cudf::logic_error); +// // static sizes +// this->run_test_col_agg(input, pwindow, fwindow, 2); // } -// /*TEST_F(RollingTestStrings, SimpleStatic) -// { -// cudf::test::strings_column_wrapper input{{"This", "is", "not", "a", "string", "type"}, -// {1, 1, 1, 0, 1, 0}}; - -// std::vector window{1}; - -// EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::MIN)); -// EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::MAX)); -// EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::COUNT_VALID)); -// EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::COUNT_ALL)); -// }*/ - -// struct RollingTestUdf : public cudf::test::BaseFixture { -// const std::string cuda_func{ -// R"***( -// template -// __device__ void CUDA_GENERIC_AGGREGATOR(OutType *ret, InType *in_col, cudf::size_type -// start, -// cudf::size_type count) { -// OutType val = 0; -// for (cudf::size_type i = 0; i < count; i++) { -// val += in_col[start + i]; -// } -// *ret = val; -// } -// )***"}; - -// const std::string ptx_func{ -// R"***( -// // -// // Generated by NVIDIA NVVM Compiler -// // -// // Compiler Build ID: CL-24817639 -// // Cuda compilation tools, release 10.0, V10.0.130 -// // Based on LLVM 3.4svn -// // - -// .version 6.3 -// .target sm_70 -// .address_size 64 - -// // .globl _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE -// .common .global .align 8 .u64 _ZN08NumbaEnv8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE; - -// .visible .func (.param .b32 func_retval0) -// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE( .param .b64 -// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0, .param .b64 -// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_1, .param .b64 -// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_2, .param .b64 -// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_3, .param .b64 -// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_4, .param .b64 -// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5, .param .b64 -// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6, .param .b64 -// _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_7 -// ) -// { -// .reg .pred %p<3>; -// .reg .b32 %r<6>; -// .reg .b64 %rd<18>; - -// ld.param.u64 %rd6, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0]; -// ld.param.u64 %rd7, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5]; -// ld.param.u64 %rd8, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6]; -// ld.param.u64 %rd9, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_7]; -// mov.u64 %rd15, 0; -// mov.u64 %rd16, %rd15; - -// BB0_1: -// mov.u64 %rd2, %rd16; -// mov.u32 %r5, 0; -// setp.ge.s64 %p1, %rd15, %rd8; -// mov.u64 %rd17, %rd15; -// @%p1 bra BB0_3; - -// mul.lo.s64 %rd12, %rd15, %rd9; -// add.s64 %rd13, %rd12, %rd7; -// ld.u32 %r5, [%rd13]; -// add.s64 %rd17, %rd15, 1; - -// BB0_3: -// cvt.s64.s32 %rd14, %r5; -// add.s64 %rd16, %rd14, %rd2; -// setp.lt.s64 %p2, %rd15, %rd8; -// mov.u64 %rd15, %rd17; -// @%p2 bra BB0_1; - -// st.u64 [%rd6], %rd2; -// mov.u32 %r4, 0; -// st.param.b32 [func_retval0+0], %r4; -// ret; -// } -// )***"}; -// }; - -// TEST_F(RollingTestUdf, StaticWindow) -// { -// size_type size = 1000; +// ------------- non-fixed-width types -------------------- -// fixed_width_column_wrapper input(thrust::make_counting_iterator(0), -// thrust::make_counting_iterator(size), -// thrust::make_constant_iterator(true)); +using RollingTestStrings = RollingTest; -// std::unique_ptr output; +TEST_F(RollingTestStrings, StringsUnsupportedOperators) +{ + cudf::test::strings_column_wrapper input{{"This", "is", "not", "a", "string", "type"}, + {1, 1, 1, 0, 1, 0}}; + + std::vector window{1}; + + EXPECT_THROW( + cudf::rolling_window(input, 2, 2, 0, *cudf::make_sum_aggregation()), + cudf::logic_error); + EXPECT_THROW( + cudf::rolling_window(input, 2, 2, 0, *cudf::make_mean_aggregation()), + cudf::logic_error); + EXPECT_THROW(cudf::rolling_window(input, + 2, + 2, + 0, + *cudf::make_udf_aggregation( + cudf::udf_type::PTX, std::string{}, cudf::data_type{})), + cudf::logic_error); + EXPECT_THROW(cudf::rolling_window(input, + 2, + 2, + 0, + *cudf::make_udf_aggregation( + cudf::udf_type::CUDA, std::string{}, cudf::data_type{})), + cudf::logic_error); +} -// auto start = cudf::detail::make_counting_transform_iterator(0, [size](size_type row) { -// return std::accumulate(thrust::make_counting_iterator(std::max(0, row - 2 + 1)), -// thrust::make_counting_iterator(std::min(size, row + 2 + 1)), -// 0); -// }); +/*TEST_F(RollingTestStrings, SimpleStatic) +{ + cudf::test::strings_column_wrapper input{{"This", "is", "not", "a", "string", "type"}, + {1, 1, 1, 0, 1, 0}}; + + std::vector window{1}; + + EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::MIN)); + EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::MAX)); + EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::COUNT_VALID)); + EXPECT_NO_THROW(this->run_test_col(input, window, window, 0, rolling_operator::COUNT_ALL)); +}*/ + +struct RollingTestUdf : public cudf::test::BaseFixture { + const std::string cuda_func{ + R"***( + template + __device__ void CUDA_GENERIC_AGGREGATOR(OutType *ret, InType *in_col, cudf::size_type + start, + cudf::size_type count) { + OutType val = 0; + for (cudf::size_type i = 0; i < count; i++) { + val += in_col[start + i]; + } + *ret = val; + } + )***"}; + + const std::string ptx_func{ + R"***( + // + // Generated by NVIDIA NVVM Compiler + // + // Compiler Build ID: CL-24817639 + // Cuda compilation tools, release 10.0, V10.0.130 + // Based on LLVM 3.4svn + // + + .version 6.3 + .target sm_70 + .address_size 64 + + // .globl _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE + .common .global .align 8 .u64 _ZN08NumbaEnv8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE; + + .visible .func (.param .b32 func_retval0) + _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE( .param .b64 + _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0, .param .b64 + _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_1, .param .b64 + _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_2, .param .b64 + _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_3, .param .b64 + _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_4, .param .b64 + _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5, .param .b64 + _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6, .param .b64 + _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_7 + ) + { + .reg .pred %p<3>; + .reg .b32 %r<6>; + .reg .b64 %rd<18>; + + ld.param.u64 %rd6, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0]; + ld.param.u64 %rd7, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5]; + ld.param.u64 %rd8, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6]; + ld.param.u64 %rd9, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_7]; + mov.u64 %rd15, 0; + mov.u64 %rd16, %rd15; + + BB0_1: + mov.u64 %rd2, %rd16; + mov.u32 %r5, 0; + setp.ge.s64 %p1, %rd15, %rd8; + mov.u64 %rd17, %rd15; + @%p1 bra BB0_3; + + mul.lo.s64 %rd12, %rd15, %rd9; + add.s64 %rd13, %rd12, %rd7; + ld.u32 %r5, [%rd13]; + add.s64 %rd17, %rd15, 1; + + BB0_3: + cvt.s64.s32 %rd14, %r5; + add.s64 %rd16, %rd14, %rd2; + setp.lt.s64 %p2, %rd15, %rd8; + mov.u64 %rd15, %rd17; + @%p2 bra BB0_1; + + st.u64 [%rd6], %rd2; + mov.u32 %r4, 0; + st.param.b32 [func_retval0+0], %r4; + ret; + } + )***"}; +}; -// auto valid = cudf::detail::make_counting_transform_iterator( -// 0, [size](size_type row) { return (row != 0 && row != size - 2 && row != size - 1); }); +TEST_F(RollingTestUdf, StaticWindow) +{ + size_type size = 1000; -// fixed_width_column_wrapper expected{start, start + size, valid}; + fixed_width_column_wrapper input(thrust::make_counting_iterator(0), + thrust::make_counting_iterator(size), + thrust::make_constant_iterator(true)); -// // Test CUDA UDF -// auto cuda_udf_agg = cudf::make_udf_aggregation( -// cudf::udf_type::CUDA, this->cuda_func, cudf::data_type{cudf::type_id::INT64}); + std::unique_ptr output; -// output = cudf::rolling_window(input, 2, 2, 4, *cuda_udf_agg); + auto start = cudf::detail::make_counting_transform_iterator(0, [size](size_type row) { + return std::accumulate(thrust::make_counting_iterator(std::max(0, row - 2 + 1)), + thrust::make_counting_iterator(std::min(size, row + 2 + 1)), + 0); + }); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); + auto valid = cudf::detail::make_counting_transform_iterator( + 0, [size](size_type row) { return (row != 0 && row != size - 2 && row != size - 1); }); -// // Test NUMBA UDF -// auto ptx_udf_agg = cudf::make_udf_aggregation( -// cudf::udf_type::PTX, this->ptx_func, cudf::data_type{cudf::type_id::INT64}); + fixed_width_column_wrapper expected{start, start + size, valid}; -// output = cudf::rolling_window(input, 2, 2, 4, *ptx_udf_agg); + // Test CUDA UDF + auto cuda_udf_agg = cudf::make_udf_aggregation( + cudf::udf_type::CUDA, this->cuda_func, cudf::data_type{cudf::type_id::INT64}); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); -// } + output = cudf::rolling_window(input, 2, 2, 4, *cuda_udf_agg); -// TEST_F(RollingTestUdf, DynamicWindow) -// { -// size_type size = 1000; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); + + // Test NUMBA UDF + auto ptx_udf_agg = cudf::make_udf_aggregation( + cudf::udf_type::PTX, this->ptx_func, cudf::data_type{cudf::type_id::INT64}); -// fixed_width_column_wrapper input(thrust::make_counting_iterator(0), -// thrust::make_counting_iterator(size), -// thrust::make_constant_iterator(true)); + output = cudf::rolling_window(input, 2, 2, 4, *ptx_udf_agg); -// auto prec = cudf::detail::make_counting_transform_iterator( -// 0, [size] __device__(size_type row) { return row % 2 + 2; }); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); +} -// auto follow = cudf::detail::make_counting_transform_iterator( -// 0, [size] __device__(size_type row) { return row % 2; }); +TEST_F(RollingTestUdf, DynamicWindow) +{ + size_type size = 1000; -// fixed_width_column_wrapper preceding(prec, prec + size); -// fixed_width_column_wrapper following(follow, follow + size); -// std::unique_ptr output; + fixed_width_column_wrapper input(thrust::make_counting_iterator(0), + thrust::make_counting_iterator(size), + thrust::make_constant_iterator(true)); -// auto start = cudf::detail::make_counting_transform_iterator(0, [size] __device__(size_type row) -// { -// return std::accumulate(thrust::make_counting_iterator(std::max(0, row - (row % 2 + 2) + 1)), -// thrust::make_counting_iterator(std::min(size, row + (row % 2) + 1)), -// 0); -// }); + auto prec = cudf::detail::make_counting_transform_iterator( + 0, [size] __device__(size_type row) { return row % 2 + 2; }); -// auto valid = cudf::detail::make_counting_transform_iterator( -// 0, [size] __device__(size_type row) { return row != 0; }); + auto follow = cudf::detail::make_counting_transform_iterator( + 0, [size] __device__(size_type row) { return row % 2; }); -// fixed_width_column_wrapper expected{start, start + size, valid}; + fixed_width_column_wrapper preceding(prec, prec + size); + fixed_width_column_wrapper following(follow, follow + size); + std::unique_ptr output; -// // Test CUDA UDF -// auto cuda_udf_agg = cudf::make_udf_aggregation( -// cudf::udf_type::CUDA, this->cuda_func, cudf::data_type{cudf::type_id::INT64}); + auto start = cudf::detail::make_counting_transform_iterator(0, [size] __device__(size_type row) { + return std::accumulate(thrust::make_counting_iterator(std::max(0, row - (row % 2 + 2) + 1)), + thrust::make_counting_iterator(std::min(size, row + (row % 2) + 1)), + 0); + }); -// output = cudf::rolling_window(input, preceding, following, 2, *cuda_udf_agg); + auto valid = cudf::detail::make_counting_transform_iterator( + 0, [size] __device__(size_type row) { return row != 0; }); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); + fixed_width_column_wrapper expected{start, start + size, valid}; -// // Test PTX UDF -// auto ptx_udf_agg = cudf::make_udf_aggregation( -// cudf::udf_type::PTX, this->ptx_func, cudf::data_type{cudf::type_id::INT64}); + // Test CUDA UDF + auto cuda_udf_agg = cudf::make_udf_aggregation( + cudf::udf_type::CUDA, this->cuda_func, cudf::data_type{cudf::type_id::INT64}); -// output = cudf::rolling_window(input, preceding, following, 2, *ptx_udf_agg); + output = cudf::rolling_window(input, preceding, following, 2, *cuda_udf_agg); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); -// } + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); -// template -// struct FixedPointTests : public cudf::test::BaseFixture { -// }; + // Test PTX UDF + auto ptx_udf_agg = cudf::make_udf_aggregation( + cudf::udf_type::PTX, this->ptx_func, cudf::data_type{cudf::type_id::INT64}); -// TYPED_TEST_CASE(FixedPointTests, cudf::test::FixedPointTypes); + output = cudf::rolling_window(input, preceding, following, 2, *ptx_udf_agg); -// TYPED_TEST(FixedPointTests, MinMaxCountLagLead) -// { -// using namespace numeric; -// using namespace cudf; -// using decimalXX = TypeParam; -// using RepType = cudf::device_storage_type_t; -// using fp_wrapper = cudf::test::fixed_point_column_wrapper; -// using fw_wrapper = cudf::test::fixed_width_column_wrapper; - -// auto const scale = scale_type{-1}; -// auto const input = fp_wrapper{{42, 1729, 55, 3, 1, 2}, {1, 1, 1, 1, 1, 1}, scale}; -// auto const expected_min = fp_wrapper{{42, 42, 3, 1, 1, 1}, {1, 1, 1, 1, 1, 1}, scale}; -// auto const expected_max = fp_wrapper{{1729, 1729, 1729, 55, 3, 2}, {1, 1, 1, 1, 1, 1}, scale}; -// auto const expected_lag = fp_wrapper{{0, 42, 1729, 55, 3, 1}, {0, 1, 1, 1, 1, 1}, scale}; -// auto const expected_lead = fp_wrapper{{1729, 55, 3, 1, 2, 0}, {1, 1, 1, 1, 1, 0}, scale}; -// auto const expected_count_val = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; -// auto const expected_count_all = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; -// auto const expected_rowno = fw_wrapper{{1, 2, 2, 2, 2, 2}, {1, 1, 1, 1, 1, 1}}; -// auto const expected_rowno1 = fw_wrapper{{1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1}}; - -// auto const min = -// rolling_window(input, 2, 1, 1, *make_min_aggregation()); -// auto const max = -// rolling_window(input, 2, 1, 1, *make_max_aggregation()); -// auto const lag = -// rolling_window(input, 2, 1, 1, *make_lag_aggregation(1)); -// auto const lead = -// rolling_window(input, 2, 1, 1, *make_lead_aggregation(1)); -// auto const valid = -// rolling_window(input, 2, 1, 1, *make_count_aggregation()); -// auto const all = rolling_window( -// input, 2, 1, 1, *make_count_aggregation(null_policy::INCLUDE)); -// auto const rowno = -// rolling_window(input, 2, 1, 1, *make_row_number_aggregation()); - -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, min->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, max->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, lag->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, lead->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, valid->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, all->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno, rowno->view()); - -// // ROW_NUMBER will always return row 1 if the preceding window is set to a constant 1 -// for (int following = 1; following < 5; ++following) { -// auto const rowno1 = rolling_window( -// input, 1, following, 1, *make_row_number_aggregation()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno1, rowno1->view()); -// } -// } + CUDF_TEST_EXPECT_COLUMNS_EQUAL(*output, expected); +} -// TYPED_TEST(FixedPointTests, MinMaxCountLagLeadNulls) -// { -// using namespace numeric; -// using namespace cudf; -// using decimalXX = TypeParam; -// using RepType = cudf::device_storage_type_t; -// using fp_wrapper = cudf::test::fixed_point_column_wrapper; -// using fp64_wrapper = cudf::test::fixed_point_column_wrapper; -// using fw_wrapper = cudf::test::fixed_width_column_wrapper; - -// auto const scale = scale_type{-1}; -// auto const input = fp_wrapper{{42, 1729, 55, 343, 1, 2}, {1, 0, 1, 0, 1, 1}, -// scale}; auto const expected_sum = fp64_wrapper{{42, 97, 55, 56, 3, 3}, {1, 1, 1, 1, 1, -// 1}, scale}; auto const expected_min = fp_wrapper{{42, 42, 55, 1, 1, 1}, {1, 1, 1, 1, 1, -// 1}, scale}; auto const expected_max = fp_wrapper{{42, 55, 55, 55, 2, 2}, {1, 1, 1, 1, 1, -// 1}, scale}; auto const expected_lag = fp_wrapper{{0, 42, 1729, 55, 343, 1}, {0, 1, 0, 1, -// 0, 1}, scale}; auto const expected_lead = fp_wrapper{{1729, 55, 343, 1, 2, 0}, {0, 1, 0, -// 1, 1, 0}, scale}; auto const expected_count_val = fw_wrapper{{1, 2, 1, 2, 2, 2}, {1, 1, 1, 1, -// 1, 1}}; auto const expected_count_all = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; -// auto const expected_rowno = fw_wrapper{{1, 2, 2, 2, 2, 2}, {1, 1, 1, 1, 1, 1}}; - -// auto const sum = -// rolling_window(input, 2, 1, 1, *make_sum_aggregation()); -// auto const min = -// rolling_window(input, 2, 1, 1, *make_min_aggregation()); -// auto const max = -// rolling_window(input, 2, 1, 1, *make_max_aggregation()); -// auto const lag = -// rolling_window(input, 2, 1, 1, *make_lag_aggregation(1)); -// auto const lead = -// rolling_window(input, 2, 1, 1, *make_lead_aggregation(1)); -// auto const valid = -// rolling_window(input, 2, 1, 1, *make_count_aggregation()); -// auto const all = rolling_window( -// input, 2, 1, 1, *make_count_aggregation(null_policy::INCLUDE)); -// auto const rowno = -// rolling_window(input, 2, 1, 1, *make_row_number_aggregation()); - -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sum, sum->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, min->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, max->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, lag->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, lead->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, valid->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, all->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno, rowno->view()); -// } +template +struct FixedPointTests : public cudf::test::BaseFixture { +}; -// class RollingDictionaryTest : public cudf::test::BaseFixture { -// }; +TYPED_TEST_CASE(FixedPointTests, cudf::test::FixedPointTypes); -// TEST_F(RollingDictionaryTest, Count) -// { -// cudf::test::dictionary_column_wrapper input( -// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, -// {1, 0, 0, 1, 0, 1, 1, 1, 0}); -// fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 1}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); -// fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); -// fixed_width_column_wrapper expected_row_number({1, 2, 2, 2, 2, 2, 2, 2, 2}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); - -// auto got_count_valid = cudf::rolling_window( -// input, 2, 2, 1, *cudf::make_count_aggregation()); -// auto got_count_all = cudf::rolling_window( -// input, -// 2, -// 2, -// 1, -// *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); -// auto got_row_number = cudf::rolling_window( -// input, 2, 2, 1, *cudf::make_row_number_aggregation()); - -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_row_number, got_row_number->view()); -// } +TYPED_TEST(FixedPointTests, MinMaxCountLagLead) +{ + using namespace numeric; + using namespace cudf; + using decimalXX = TypeParam; + using RepType = cudf::device_storage_type_t; + using fp_wrapper = cudf::test::fixed_point_column_wrapper; + using fw_wrapper = cudf::test::fixed_width_column_wrapper; + + auto const scale = scale_type{-1}; + auto const input = fp_wrapper{{42, 1729, 55, 3, 1, 2}, {1, 1, 1, 1, 1, 1}, scale}; + auto const expected_min = fp_wrapper{{42, 42, 3, 1, 1, 1}, {1, 1, 1, 1, 1, 1}, scale}; + auto const expected_max = fp_wrapper{{1729, 1729, 1729, 55, 3, 2}, {1, 1, 1, 1, 1, 1}, scale}; + auto const expected_lag = fp_wrapper{{0, 42, 1729, 55, 3, 1}, {0, 1, 1, 1, 1, 1}, scale}; + auto const expected_lead = fp_wrapper{{1729, 55, 3, 1, 2, 0}, {1, 1, 1, 1, 1, 0}, scale}; + auto const expected_count_val = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; + auto const expected_count_all = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; + auto const expected_rowno = fw_wrapper{{1, 2, 2, 2, 2, 2}, {1, 1, 1, 1, 1, 1}}; + auto const expected_rowno1 = fw_wrapper{{1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1}}; + + auto const min = + rolling_window(input, 2, 1, 1, *make_min_aggregation()); + auto const max = + rolling_window(input, 2, 1, 1, *make_max_aggregation()); + auto const lag = + rolling_window(input, 2, 1, 1, *make_lag_aggregation(1)); + auto const lead = + rolling_window(input, 2, 1, 1, *make_lead_aggregation(1)); + auto const valid = + rolling_window(input, 2, 1, 1, *make_count_aggregation()); + auto const all = rolling_window( + input, 2, 1, 1, *make_count_aggregation(null_policy::INCLUDE)); + auto const rowno = + rolling_window(input, 2, 1, 1, *make_row_number_aggregation()); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, min->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, max->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, lag->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, lead->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, valid->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, all->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno, rowno->view()); + + // ROW_NUMBER will always return row 1 if the preceding window is set to a constant 1 + for (int following = 1; following < 5; ++following) { + auto const rowno1 = rolling_window( + input, 1, following, 1, *make_row_number_aggregation()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno1, rowno1->view()); + } +} -// TEST_F(RollingDictionaryTest, MinMax) -// { -// cudf::test::dictionary_column_wrapper input( -// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, -// {1, 0, 0, 1, 0, 1, 1, 1, 0}); -// cudf::test::strings_column_wrapper expected_min( -// {"This", "This", "test", "operated", "on", "on", "on", "on", "string"}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); -// cudf::test::strings_column_wrapper expected_max( -// {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, -// {1, 1, 1, 1, 1, 1, 1, 1, 1}); - -// auto got_min_dict = -// cudf::rolling_window(input, 2, 2, 1, -// *cudf::make_min_aggregation()); -// auto got_min = cudf::dictionary::decode(cudf::dictionary_column_view(got_min_dict->view())); - -// auto got_max_dict = -// cudf::rolling_window(input, 2, 2, 1, -// *cudf::make_max_aggregation()); -// auto got_max = cudf::dictionary::decode(cudf::dictionary_column_view(got_max_dict->view())); - -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); -// } +TYPED_TEST(FixedPointTests, MinMaxCountLagLeadNulls) +{ + using namespace numeric; + using namespace cudf; + using decimalXX = TypeParam; + using RepType = cudf::device_storage_type_t; + using fp_wrapper = cudf::test::fixed_point_column_wrapper; + using fp64_wrapper = cudf::test::fixed_point_column_wrapper; + using fw_wrapper = cudf::test::fixed_width_column_wrapper; + + auto const scale = scale_type{-1}; + auto const input = fp_wrapper{{42, 1729, 55, 343, 1, 2}, {1, 0, 1, 0, 1, 1}, scale}; + auto const expected_sum = fp64_wrapper{{42, 97, 55, 56, 3, 3}, {1, 1, 1, 1, 1, 1}, scale}; + auto const expected_min = fp_wrapper{{42, 42, 55, 1, 1, 1}, {1, 1, 1, 1, 1, 1}, scale}; + auto const expected_max = fp_wrapper{{42, 55, 55, 55, 2, 2}, {1, 1, 1, 1, 1, 1}, scale}; + auto const expected_lag = fp_wrapper{{0, 42, 1729, 55, 343, 1}, {0, 1, 0, 1, 0, 1}, scale}; + auto const expected_lead = fp_wrapper{{1729, 55, 343, 1, 2, 0}, {0, 1, 0, 1, 1, 0}, scale}; + auto const expected_count_val = fw_wrapper{{1, 2, 1, 2, 2, 2}, {1, 1, 1, 1, 1, 1}}; + auto const expected_count_all = fw_wrapper{{2, 3, 3, 3, 3, 2}, {1, 1, 1, 1, 1, 1}}; + auto const expected_rowno = fw_wrapper{{1, 2, 2, 2, 2, 2}, {1, 1, 1, 1, 1, 1}}; + + auto const sum = + rolling_window(input, 2, 1, 1, *make_sum_aggregation()); + auto const min = + rolling_window(input, 2, 1, 1, *make_min_aggregation()); + auto const max = + rolling_window(input, 2, 1, 1, *make_max_aggregation()); + auto const lag = + rolling_window(input, 2, 1, 1, *make_lag_aggregation(1)); + auto const lead = + rolling_window(input, 2, 1, 1, *make_lead_aggregation(1)); + auto const valid = + rolling_window(input, 2, 1, 1, *make_count_aggregation()); + auto const all = rolling_window( + input, 2, 1, 1, *make_count_aggregation(null_policy::INCLUDE)); + auto const rowno = + rolling_window(input, 2, 1, 1, *make_row_number_aggregation()); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sum, sum->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, min->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, max->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, lag->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, lead->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, valid->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, all->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno, rowno->view()); +} -// TEST_F(RollingDictionaryTest, LeadLag) -// { -// cudf::test::dictionary_column_wrapper input( -// {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, -// {1, 0, 0, 1, 0, 1, 1, 1, 0}); -// cudf::test::strings_column_wrapper expected_lead( -// {"", "", "test", "", "operated", "on", "string", "", ""}, {0, 0, 1, 0, 1, 1, 1, 0, 0}); -// cudf::test::strings_column_wrapper expected_lag( -// {"", "This", "", "", "test", "", "operated", "on", "string"}, {0, 1, 0, 0, 1, 0, 1, 1, 1}); - -// auto got_lead_dict = cudf::rolling_window( -// input, 2, 1, 1, *cudf::make_lead_aggregation(1)); -// auto got_lead = cudf::dictionary::decode(cudf::dictionary_column_view(got_lead_dict->view())); - -// auto got_lag_dict = -// cudf::rolling_window(input, 2, 2, 1, -// *cudf::make_lag_aggregation(1)); -// auto got_lag = cudf::dictionary::decode(cudf::dictionary_column_view(got_lag_dict->view())); - -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, got_lead->view()); -// CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, got_lag->view()); -// } +class RollingDictionaryTest : public cudf::test::BaseFixture { +}; + +TEST_F(RollingDictionaryTest, Count) +{ + cudf::test::dictionary_column_wrapper input( + {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, + {1, 0, 0, 1, 0, 1, 1, 1, 0}); + fixed_width_column_wrapper expected_count_val({1, 2, 1, 2, 3, 3, 3, 2, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + fixed_width_column_wrapper expected_count_all({3, 4, 4, 4, 4, 4, 4, 3, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + fixed_width_column_wrapper expected_row_number({1, 2, 2, 2, 2, 2, 2, 2, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + + auto got_count_valid = cudf::rolling_window( + input, 2, 2, 1, *cudf::make_count_aggregation()); + auto got_count_all = cudf::rolling_window( + input, + 2, + 2, + 1, + *cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); + auto got_row_number = cudf::rolling_window( + input, 2, 2, 1, *cudf::make_row_number_aggregation()); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_val, got_count_valid->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_count_all, got_count_all->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_row_number, got_row_number->view()); +} + +TEST_F(RollingDictionaryTest, MinMax) +{ + cudf::test::dictionary_column_wrapper input( + {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, + {1, 0, 0, 1, 0, 1, 1, 1, 0}); + cudf::test::strings_column_wrapper expected_min( + {"This", "This", "test", "operated", "on", "on", "on", "on", "string"}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + cudf::test::strings_column_wrapper expected_max( + {"This", "test", "test", "test", "test", "string", "string", "string", "string"}, + {1, 1, 1, 1, 1, 1, 1, 1, 1}); + + auto got_min_dict = + cudf::rolling_window(input, 2, 2, 1, *cudf::make_min_aggregation()); + auto got_min = cudf::dictionary::decode(cudf::dictionary_column_view(got_min_dict->view())); + + auto got_max_dict = + cudf::rolling_window(input, 2, 2, 1, *cudf::make_max_aggregation()); + auto got_max = cudf::dictionary::decode(cudf::dictionary_column_view(got_max_dict->view())); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_min, got_min->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_max, got_max->view()); +} + +TEST_F(RollingDictionaryTest, LeadLag) +{ + cudf::test::dictionary_column_wrapper input( + {"This", "is", "rolling", "test", "being", "operated", "on", "string", "column"}, + {1, 0, 0, 1, 0, 1, 1, 1, 0}); + cudf::test::strings_column_wrapper expected_lead( + {"", "", "test", "", "operated", "on", "string", "", ""}, {0, 0, 1, 0, 1, 1, 1, 0, 0}); + cudf::test::strings_column_wrapper expected_lag( + {"", "This", "", "", "test", "", "operated", "on", "string"}, {0, 1, 0, 0, 1, 0, 1, 1, 1}); + + auto got_lead_dict = cudf::rolling_window( + input, 2, 1, 1, *cudf::make_lead_aggregation(1)); + auto got_lead = cudf::dictionary::decode(cudf::dictionary_column_view(got_lead_dict->view())); + + auto got_lag_dict = + cudf::rolling_window(input, 2, 2, 1, *cudf::make_lag_aggregation(1)); + auto got_lag = cudf::dictionary::decode(cudf::dictionary_column_view(got_lag_dict->view())); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lead, got_lead->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lag, got_lag->view()); +} CUDF_TEST_PROGRAM_MAIN() From 16cb7a8155405f624787ac062c0fb710115426e7 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 22 Jul 2021 21:16:09 -0700 Subject: [PATCH 05/47] clean up --- cpp/tests/rolling/rolling_test.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 55eb87208b7..e7b85d679e1 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -926,19 +926,6 @@ TYPED_TEST(RollingTest, RandomDynamicWithInvalid) this->run_test_col_agg(input, preceding_window, following_window, max_window_size); } -// TYPED_TEST(RollingTest, ContrivedVar) -// { -// // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html -// auto const col_data = cudf::test::make_type_param_vector({0, 1, 2, 0, -// 4}); const std::vector col_mask = {1, 1, 1, 1, 1}; - -// fixed_width_column_wrapper input(col_data.begin(), col_data.end(), -// col_mask.begin()); std::vector pwindow{2}; std::vector fwindow{0}; - -// // static sizes -// this->run_test_col_agg(input, pwindow, fwindow, 2); -// } - // ------------- non-fixed-width types -------------------- using RollingTestStrings = RollingTest; From ddd59f0f8c97e470a63d6a6b1658b5633fbb0383 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 22 Jul 2021 21:25:29 -0700 Subject: [PATCH 06/47] header cleanup --- cpp/src/rolling/rolling_detail.cuh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 11aac0a1659..137c5b5fe2c 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -16,16 +16,11 @@ #pragma once -#include "cudf/detail/null_mask.hpp" -#include "cudf/detail/unary.hpp" -#include "cudf/utilities/type_dispatcher.hpp" #include "lead_lag_nested_detail.cuh" -#include "rmm/exec_policy.hpp" #include "rolling/rolling_collect_list.cuh" #include "rolling/rolling_detail.hpp" #include "rolling/rolling_jit_detail.hpp" #include "rolling_detail.hpp" -#include "thrust/pair.h" #include #include @@ -38,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -59,12 +55,14 @@ #include #include +#include #include #include #include #include #include +#include #include #include From bcd00f0a6d689f818c679f993ce98cca8d1ff01d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 22 Jul 2021 21:26:50 -0700 Subject: [PATCH 07/47] . --- cpp/src/rolling/rolling_detail.cuh | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 137c5b5fe2c..46f62ac2aa8 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -67,8 +67,6 @@ #include -#undef DEBUG - namespace cudf { namespace detail { From da8b7552b752224e52434fd4c71a07aea46916cf Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 22 Jul 2021 21:29:23 -0700 Subject: [PATCH 08/47] More cleanup --- cpp/src/rolling/rolling_detail.cuh | 37 ++---------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 46f62ac2aa8..0b1c019e49c 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -327,43 +327,10 @@ struct DeviceRollingVariance { // Variance/Std is non-negative, thus ddof should be strictly less than valid counts. // Variance/Std of a lone value is statistically meaningless bool output_is_valid = (count >= min_periods) and not(count == 1) and not(count <= ddof); -#ifdef DEBUG - printf("count: %d\n", count); -#endif if (output_is_valid) { - // OutputType mean = thrust::reduce(thrust::seq, - // thrust::make_counting_iterator(start_index), - // thrust::make_counting_iterator(end_index), - // OutputType{0}, - // [&](auto acc, auto i) { - // if (has_nulls) { - // return input.is_valid_nocheck(i) - // ? acc + input.element(i) - // : acc; - // } else { - // return acc + input.element(i); - // } - // }) / - // count; - - // output.element(current_index) = - // thrust::reduce(thrust::seq, - // thrust::make_counting_iterator(start_index), - // thrust::make_counting_iterator(end_index), - // OutputType{0}, - // [&](auto acc, auto i) { - // auto x = input.element(i); - // if (has_nulls) { - // return input.is_valid_nocheck(i) ? acc + (x - mean) * (x - mean) : - // acc; - // } else { - // return acc + (x - mean) * (x - mean); - // } - // }) / - // (count - ddof); - - // Welford algorithm + // Welford algorithm, a numerically stable, single pass algorithm + // See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance OutputType m, m2; size_type running_count; From 3ff0d8d32329d79eed0d0d939358bb762f95a833 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 22 Jul 2021 21:36:58 -0700 Subject: [PATCH 09/47] revert ptx changes --- cpp/tests/rolling/rolling_test.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index e7b85d679e1..8b12b449618 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -976,8 +976,7 @@ struct RollingTestUdf : public cudf::test::BaseFixture { const std::string cuda_func{ R"***( template - __device__ void CUDA_GENERIC_AGGREGATOR(OutType *ret, InType *in_col, cudf::size_type - start, + __device__ void CUDA_GENERIC_AGGREGATOR(OutType *ret, InType *in_col, cudf::size_type start, cudf::size_type count) { OutType val = 0; for (cudf::size_type i = 0; i < count; i++) { @@ -1004,22 +1003,22 @@ struct RollingTestUdf : public cudf::test::BaseFixture { // .globl _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE .common .global .align 8 .u64 _ZN08NumbaEnv8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE; - .visible .func (.param .b32 func_retval0) - _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE( .param .b64 - _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0, .param .b64 - _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_1, .param .b64 - _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_2, .param .b64 - _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_3, .param .b64 - _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_4, .param .b64 - _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5, .param .b64 - _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6, .param .b64 - _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_7 + .visible .func (.param .b32 func_retval0) _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE( + .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0, + .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_1, + .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_2, + .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_3, + .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_4, + .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5, + .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6, + .param .b64 _ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_7 ) { .reg .pred %p<3>; .reg .b32 %r<6>; .reg .b64 %rd<18>; + ld.param.u64 %rd6, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_0]; ld.param.u64 %rd7, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_5]; ld.param.u64 %rd8, [_ZN8__main__7add$241E5ArrayIiLi1E1A7mutable7alignedE_paam_6]; From fc00d8f836dc32dfd2796d108ac056155b7355c9 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 23 Jul 2021 13:04:17 -0700 Subject: [PATCH 10/47] Static tests --- cpp/include/cudf_test/type_lists.hpp | 14 +++- cpp/src/rolling/rolling_detail.cuh | 2 +- cpp/tests/rolling/rolling_test.cpp | 109 +++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 2 deletions(-) diff --git a/cpp/include/cudf_test/type_lists.hpp b/cpp/include/cudf_test/type_lists.hpp index 5c1b0c6c458..74688b7f133 100644 --- a/cpp/include/cudf_test/type_lists.hpp +++ b/cpp/include/cudf_test/type_lists.hpp @@ -287,11 +287,23 @@ using FixedWidthTypes = Concat; * Example: * ``` * // Invokes all typed fixture tests for all fixed-width types in libcudf - * TYPED_TEST_CASE(MyTypedFixture, cudf::test::FixedWidthTypes); + * TYPED_TEST_CASE(MyTypedFixture, cudf::test::FixedWidthTypesWithoutFixedPoint); * ``` */ using FixedWidthTypesWithoutFixedPoint = Concat; +/** + * @brief Provides a list of all fixed-width element types except for the + * chrono types for use in GTest typed tests. + * + * Example: + * ``` + * // Invokes all typed fixture tests for all fixed-width types in libcudf + * TYPED_TEST_CASE(MyTypedFixture, cudf::test::FixedWidthTypesWithoutChrono); + * ``` + */ +using FixedWidthTypesWithoutChrono = Concat; + /** * @brief Provides a list of sortable types for use in GTest typed tests. * diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 0b1c019e49c..bcc06ef37b8 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -325,7 +325,7 @@ struct DeviceRollingVariance { } // Variance/Std is non-negative, thus ddof should be strictly less than valid counts. - // Variance/Std of a lone value is statistically meaningless + // Variance/Std of a lone value is undefined. bool output_is_valid = (count >= min_periods) and not(count == 1) and not(count <= ddof); if (output_is_valid) { diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 8b12b449618..55d935a5f8e 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -520,6 +521,15 @@ class RollingTest : public cudf::test::BaseFixture { } }; +template +class RollingVarStdTest : public cudf::test::BaseFixture { +}; + +TYPED_TEST_CASE(RollingVarStdTest, cudf::test::FixedWidthTypesWithoutChrono); + +class RollingtVarStdTestUntyped : public cudf::test::BaseFixture { +}; + // // ------------- expected failures -------------------- class RollingErrorTest : public cudf::test::BaseFixture { @@ -726,6 +736,105 @@ TYPED_TEST(RollingTest, SimpleStatic) this->run_test_col_agg(input, window, window, 1); } +TYPED_TEST(RollingVarStdTest, SimpleStaticVarianceStd) +{ +#define XXX 0 // NULL stub + + using ResultType = double; + + size_type const ddof = 1, min_periods = 1, preceding_window = 2, following_window = 1; + + auto const col_data = + cudf::test::make_type_param_vector({XXX, XXX, 9, 5, XXX, XXX, 0, 8, 5, 8}); + const std::vector col_mask = {0, 0, 1, 1, 0, 0, 1, 1, 1, 1}; + + auto const expected_var = + cudf::is_boolean() + ? std::vector{XXX, XXX, 0, 0, XXX, XXX, 0.5, 0.33333333333333337, 0, 0} + : std::vector{XXX, XXX, 8, 8, XXX, XXX, 32, 16.333333333333332, 3, 4.5}; + std::vector expected_std(expected_var.size()); + std::transform(expected_var.begin(), expected_var.end(), expected_std.begin(), [](auto const& x) { + return std::sqrt(x); + }); + + const std::vector expected_mask = {0, 0, 1, 1, 0, 0, 1, 1, 1, 1}; + + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); + fixed_width_column_wrapper var_expect( + expected_var.begin(), expected_var.end(), expected_mask.begin()); + fixed_width_column_wrapper std_expect( + expected_std.begin(), expected_std.end(), expected_mask.begin()); + + std::unique_ptr var_result, std_result; + // static sizes + EXPECT_NO_THROW(var_result = cudf::rolling_window(input, + preceding_window, + following_window, + min_periods, + dynamic_cast( + *cudf::make_variance_aggregation(ddof)));); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*var_result, var_expect); + + EXPECT_NO_THROW(std_result = cudf::rolling_window(input, + preceding_window, + following_window, + min_periods, + dynamic_cast( + *cudf::make_std_aggregation(ddof)));); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*std_result, std_expect); + +#undef XXX +} + +TEST_F(RollingtVarStdTestUntyped, SimpleStaticVarianceStdInf) +{ +#define XXX 0. // NULL stub + + using ResultType = double; + + double const inf = std::numeric_limits::infinity(); + double const nan = std::numeric_limits::signaling_NaN(); + size_type const ddof = 1, min_periods = 1, preceding_window = 3, following_window = 0; + + auto const col_data = + cudf::test::make_type_param_vector({5., 4., XXX, inf, 4., 8., 0., XXX, XXX, 5.}); + const std::vector col_mask = {1, 1, 0, 1, 1, 1, 1, 0, 0, 1}; + + auto const expected_var = std::vector{XXX, 0.5, 0.5, nan, nan, nan, 16, 32, XXX, XXX}; + std::vector expected_std(expected_var.size()); + std::transform(expected_var.begin(), expected_var.end(), expected_std.begin(), [](auto const& x) { + return std::sqrt(x); + }); + + const std::vector expected_mask = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0}; + + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); + fixed_width_column_wrapper var_expect( + expected_var.begin(), expected_var.end(), expected_mask.begin()); + fixed_width_column_wrapper std_expect( + expected_std.begin(), expected_std.end(), expected_mask.begin()); + + std::unique_ptr var_result, std_result; + // static sizes + EXPECT_NO_THROW(var_result = cudf::rolling_window(input, + preceding_window, + following_window, + min_periods, + dynamic_cast( + *cudf::make_variance_aggregation(ddof)));); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*var_result, var_expect); + + EXPECT_NO_THROW(std_result = cudf::rolling_window(input, + preceding_window, + following_window, + min_periods, + dynamic_cast( + *cudf::make_std_aggregation(ddof)));); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*std_result, std_expect); + +#undef XXX +} + // negative sizes TYPED_TEST(RollingTest, NegativeWindowSizes) { From 1259df6be5ebad655e03eef8ad5ccf7f64f47e72 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 23 Jul 2021 14:04:28 -0700 Subject: [PATCH 11/47] undo python changes --- cpp/src/rolling/rolling_detail.cuh | 1 - python/cudf/cudf/_lib/aggregation.pyx | 8 -------- python/cudf/cudf/core/window/rolling.py | 3 --- 3 files changed, 12 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index bcc06ef37b8..1c76eb3b8fe 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -354,7 +354,6 @@ struct DeviceRollingVariance { m2_acc += tmp1 * tmp2; return thrust::make_tuple(r_count_acc, m_acc, m2_acc); }); - output.element(current_index) = m2 / (count - ddof); } diff --git a/python/cudf/cudf/_lib/aggregation.pyx b/python/cudf/cudf/_lib/aggregation.pyx index 1241c168cd2..4c94452c73d 100644 --- a/python/cudf/cudf/_lib/aggregation.pyx +++ b/python/cudf/cudf/_lib/aggregation.pyx @@ -382,14 +382,6 @@ cdef class RollingAggregation: libcudf_aggregation.make_collect_list_aggregation[ rolling_aggregation]()) return agg - - @classmethod - def std(cls, ddof=1): - cdef RollingAggregation agg = cls() - agg.c_obj = move( - libcudf_aggregation.make_std_aggregation[ - rolling_aggregation](ddof)) - return agg @classmethod def from_udf(cls, op, *args, **kwargs): diff --git a/python/cudf/cudf/core/window/rolling.py b/python/cudf/cudf/core/window/rolling.py index dd179a83d6a..d9a2fd89165 100644 --- a/python/cudf/cudf/core/window/rolling.py +++ b/python/cudf/cudf/core/window/rolling.py @@ -245,9 +245,6 @@ def mean(self): def count(self): return self._apply_agg("count") - - def std(self): - return self._apply_agg("std") def apply(self, func, *args, **kwargs): """ From 3539e1b1d60c15e8d294fd1eaf379a8ee2eaa89d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 23 Jul 2021 14:55:40 -0700 Subject: [PATCH 12/47] . --- cpp/tests/rolling/rolling_test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 55d935a5f8e..8658e54bcdc 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -35,7 +35,6 @@ #include #include -#include #include using cudf::bitmask_type; From 07aa54d6df53fd630b3b7c529aeab5bf9a1a0c69 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 23 Jul 2021 15:09:47 -0700 Subject: [PATCH 13/47] docs --- cpp/include/cudf/aggregation.hpp | 10 ++++++++++ cpp/include/cudf/rolling.hpp | 9 ++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cpp/include/cudf/aggregation.hpp b/cpp/include/cudf/aggregation.hpp index 5d672cc0502..c760f4fe3d7 100644 --- a/cpp/include/cudf/aggregation.hpp +++ b/cpp/include/cudf/aggregation.hpp @@ -179,6 +179,11 @@ std::unique_ptr make_m2_aggregation(); /** * @brief Factory to create a VARIANCE aggregation * + * In rolling window aggregation, when there is only one valid observation + * in the window, the result is null. + * + * Chrono type is unsupported in rolling window VARIANCE aggregation. + * * @param ddof Delta degrees of freedom. The divisor used in calculation of * `variance` is `N - ddof`, where `N` is the population size. */ @@ -188,6 +193,11 @@ std::unique_ptr make_variance_aggregation(size_type ddof = 1); /** * @brief Factory to create a STD aggregation * + * In rolling window aggregation, when there is only one valid observation + * in the window, the result is null. + * + * Chrono type is unsupported in rolling window STD aggregation. + * * @param ddof Delta degrees of freedom. The divisor used in calculation of * `std` is `N - ddof`, where `N` is the population size. */ diff --git a/cpp/include/cudf/rolling.hpp b/cpp/include/cudf/rolling.hpp index 4fb1b4a7319..4373e103f3b 100644 --- a/cpp/include/cudf/rolling.hpp +++ b/cpp/include/cudf/rolling.hpp @@ -41,9 +41,12 @@ namespace cudf { * - instead of storing NA/NaN for output rows that do not meet the minimum number of observations * this function updates the valid bitmask of the column to indicate which elements are valid. * - * The returned column for count aggregation always has `INT32` type. All other operators return a - * column of the same type as the input. Therefore it is suggested to convert integer column types - * (especially low-precision integers) to `FLOAT32` or `FLOAT64` before doing a rolling `MEAN`. + * Notes on return column types: + * - The returned column for count aggregation always has `INT32` type. + * - The returned column for VARIANCE/STD aggregations always has `double` type. + * - All other operators return a column of the same type as the input. Therefore + * it is suggested to convert integer column types (especially low-precision integers) + * to `FLOAT32` or `FLOAT64` before doing a rolling `MEAN`. * * @param[in] input_col The input column * @param[in] preceding_window The static rolling window size in the backward direction. From d250115a153e21051bde11b56b3d446c20da1b73 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sun, 25 Jul 2021 11:27:15 -0700 Subject: [PATCH 14/47] initial --- python/cudf/cudf/_lib/aggregation.pyx | 14 ++++++ python/cudf/cudf/_lib/rolling.pyx | 5 +- python/cudf/cudf/core/window/rolling.py | 11 +++++ python/cudf/cudf/testing/dataset_generator.py | 16 +++++++ python/cudf/cudf/tests/test_rolling.py | 48 ++++++++++++++++++- 5 files changed, 90 insertions(+), 4 deletions(-) diff --git a/python/cudf/cudf/_lib/aggregation.pyx b/python/cudf/cudf/_lib/aggregation.pyx index 4c94452c73d..e8e46fd7430 100644 --- a/python/cudf/cudf/_lib/aggregation.pyx +++ b/python/cudf/cudf/_lib/aggregation.pyx @@ -350,6 +350,20 @@ cdef class RollingAggregation: libcudf_aggregation.make_mean_aggregation[rolling_aggregation]()) return agg + @classmethod + def var(cls, ddof=1): + cdef RollingAggregation agg = cls() + agg.c_obj = move( + libcudf_aggregation.make_variance_aggregation[rolling_aggregation](ddof)) + return agg + + @classmethod + def std(cls, ddof=1): + cdef RollingAggregation agg = cls() + agg.c_obj = move( + libcudf_aggregation.make_std_aggregation[rolling_aggregation](ddof)) + return agg + @classmethod def count(cls, dropna=True): cdef libcudf_types.null_policy c_null_handling diff --git a/python/cudf/cudf/_lib/rolling.pyx b/python/cudf/cudf/_lib/rolling.pyx index 87c2fa6178f..7d5a48e7f5a 100644 --- a/python/cudf/cudf/_lib/rolling.pyx +++ b/python/cudf/cudf/_lib/rolling.pyx @@ -18,7 +18,7 @@ from cudf._lib.cpp.types cimport size_type def rolling(Column source_column, Column pre_column_window, - Column fwd_column_window, window, min_periods, center, op): + Column fwd_column_window, window, min_periods, center, op, agg_params): """ Rolling on input executing operation within the given window for each row @@ -33,6 +33,7 @@ def rolling(Column source_column, Column pre_column_window, center : Set the labels at the center of the window op : operation to be executed, as of now it supports MIN, MAX, COUNT, SUM, MEAN and UDF + agg_params : dict, parameter for the aggregation (e.g. ddof for VAR/STD) Returns ------- @@ -51,7 +52,7 @@ def rolling(Column source_column, Column pre_column_window, cython_agg = make_rolling_aggregation( op, {'dtype': source_column.dtype}) else: - cython_agg = make_rolling_aggregation(op) + cython_agg = make_rolling_aggregation(op, agg_params) if window is None: if center: diff --git a/python/cudf/cudf/core/window/rolling.py b/python/cudf/cudf/core/window/rolling.py index d9a2fd89165..30d475d5e6a 100644 --- a/python/cudf/cudf/core/window/rolling.py +++ b/python/cudf/cudf/core/window/rolling.py @@ -174,6 +174,7 @@ def __init__( self.min_periods = min_periods self.center = center self._normalize() + self.agg_params = {} if axis != 0: raise NotImplementedError("axis != 0 is not supported yet.") self.axis = axis @@ -204,6 +205,7 @@ def _apply_agg_series(self, sr, agg_name): self.min_periods, self.center, agg_name, + self.agg_params, ) else: result_col = libcudf.rolling.rolling( @@ -214,6 +216,7 @@ def _apply_agg_series(self, sr, agg_name): self.min_periods, self.center, agg_name, + self.agg_params, ) return sr._copy_construct(data=result_col) @@ -243,6 +246,14 @@ def max(self): def mean(self): return self._apply_agg("mean") + def var(self, ddof=1): + self.agg_params["ddof"] = ddof + return self._apply_agg("var") + + def std(self, ddof=1): + self.agg_params["ddof"] = ddof + return self._apply_agg("std") + def count(self): return self._apply_agg("count") diff --git a/python/cudf/cudf/testing/dataset_generator.py b/python/cudf/cudf/testing/dataset_generator.py index 5e03068f818..600600c16f1 100644 --- a/python/cudf/cudf/testing/dataset_generator.py +++ b/python/cudf/cudf/testing/dataset_generator.py @@ -366,6 +366,22 @@ def rand_dataframe( dtype=dtype, ) ) + elif dtype == "decimal32": + max_precision = meta.get( + "max_precision", cudf.Decimal32Dtype.MAX_PRECISION + ) + precision = np.random.randint(1, max_precision) + scale = np.random.randint(0, precision) + dtype = cudf.Decimal32Dtype(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( diff --git a/python/cudf/cudf/tests/test_rolling.py b/python/cudf/cudf/tests/test_rolling.py index 07e7f43c992..ad2cd7e6692 100644 --- a/python/cudf/cudf/tests/test_rolling.py +++ b/python/cudf/cudf/tests/test_rolling.py @@ -7,6 +7,7 @@ import pytest import cudf +import cudf.testing.dataset_generator as dataset_generator from cudf.core._compat import PANDAS_GE_110 from cudf.testing._utils import assert_eq @@ -20,7 +21,9 @@ ([1, 2, 4, 9, 9, 4], ["a", "b", "c", "d", "e", "f"]), ], ) -@pytest.mark.parametrize("agg", ["sum", "min", "max", "mean", "count"]) +@pytest.mark.parametrize( + "agg", ["sum", "min", "max", "mean", "count", "std", "var"] +) @pytest.mark.parametrize("nulls", ["none", "one", "some", "all"]) @pytest.mark.parametrize("center", [True, False]) def test_rolling_series_basic(data, index, agg, nulls, center): @@ -64,7 +67,9 @@ def test_rolling_series_basic(data, index, agg, nulls, center): }, ], ) -@pytest.mark.parametrize("agg", ["sum", "min", "max", "mean", "count"]) +@pytest.mark.parametrize( + "agg", ["sum", "min", "max", "mean", "count", "std", "var"] +) @pytest.mark.parametrize("nulls", ["none", "one", "some", "all"]) @pytest.mark.parametrize("center", [True, False]) def test_rolling_dataframe_basic(data, agg, nulls, center): @@ -102,6 +107,8 @@ def test_rolling_dataframe_basic(data, agg, nulls, center): pytest.param("max"), pytest.param("mean"), pytest.param("count"), + pytest.param("std"), + pytest.param("var"), ], ) def test_rolling_with_offset(agg): @@ -124,6 +131,43 @@ def test_rolling_with_offset(agg): ) +@pytest.mark.parametrize("agg", ["std", "var"]) +@pytest.mark.parametrize("center", [True, False]) +def test_rolling_var_std_dynamic(agg, center): + if PANDAS_GE_110: + kwargs = {"check_freq": False} + else: + kwargs = {} + + n_rows = 10_000 + data = dataset_generator.rand_dataframe( + dtypes_meta=[ + {"dtype": "i4", "null_frequency": 0.4, "cardinality": 100}, + {"dtype": "f8", "null_frequency": 0.4, "cardinality": 100}, + {"dtype": "decimal64", "null_frequency": 0.4, "cardinality": 100}, + {"dtype": "decimal32", "null_frequency": 0.4, "cardinality": 100}, + ], + rows=n_rows, + use_threads=False, + ) + + pdf = data.to_pandas() + pdf["1"][ + [np.random.randint(0, n_rows - 1) for _ in range(int(n_rows * 0.2))] + ] = float("inf") + + gdf = cudf.from_pandas(pdf) + for window_size in range(1, len(data) + 1): + for min_periods in range(1, window_size + 1): + expect = getattr( + pdf.rolling(window_size, min_periods, center), agg + )().fillna(-1) + got = getattr( + gdf.rolling(window_size, min_periods, center), agg + )().fillna(-1) + assert_eq(expect, got, **kwargs) + + def test_rolling_count_with_offset(): """ This test covers the xfail case from test_rolling_with_offset["count"]. From 885d66efc8a5d3d9a24d3f13c6334205f7533a3c Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sun, 25 Jul 2021 11:42:54 -0700 Subject: [PATCH 15/47] remove count==1 restriction --- cpp/src/rolling/rolling_detail.cuh | 3 +-- cpp/tests/rolling/rolling_test.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 1c76eb3b8fe..ac2a302291c 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -325,8 +325,7 @@ struct DeviceRollingVariance { } // Variance/Std is non-negative, thus ddof should be strictly less than valid counts. - // Variance/Std of a lone value is undefined. - bool output_is_valid = (count >= min_periods) and not(count == 1) and not(count <= ddof); + bool output_is_valid = (count >= min_periods) and not(count <= ddof); if (output_is_valid) { // Welford algorithm, a numerically stable, single pass algorithm diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 8658e54bcdc..5f9e4f8f9d9 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -355,7 +355,7 @@ class RollingTest : public cudf::test::BaseFixture { for (auto j = start_index; j < end_index; j++) { if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { count++; } } - ref_valid[i] = (count >= min_periods) and not(count == 1) and not(count == ddof); + ref_valid[i] = (count >= min_periods) and not(count == ddof); if (ref_valid[i]) { OutputType mean{0}, m2{0}, tmp1, tmp2; From 68ab4ae508ad730562a930433d2245875bf8b4fe Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Sun, 25 Jul 2021 15:26:46 -0700 Subject: [PATCH 16/47] add ddof tests --- cpp/tests/rolling/rolling_test.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 5f9e4f8f9d9..f1bc80d1e4b 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -256,16 +256,24 @@ class RollingTest : public cudf::test::BaseFixture { } if (cudf::is_fixed_width(input.type()) and not cudf::is_chrono(input.type())) { - run_test_col(input, - preceding_window, - following_window, - min_periods, - *cudf::make_variance_aggregation()); - run_test_col(input, - preceding_window, - following_window, - min_periods, - *cudf::make_std_aggregation()); + size_type min_window_size = preceding_window[0] + following_window[0]; + for (size_t i = 0; i < preceding_window.size(); i++) { + min_window_size = std::min(min_window_size, preceding_window[i] + following_window[i]); + } + + for (size_type ddof = 0; ddof < min_window_size; ddof *= 2) { + run_test_col(input, + preceding_window, + following_window, + min_periods, + *cudf::make_variance_aggregation(ddof)); + run_test_col(input, + preceding_window, + following_window, + min_periods, + *cudf::make_std_aggregation(ddof)); + if (ddof == 0) { ddof++; } + } } } @@ -355,7 +363,7 @@ class RollingTest : public cudf::test::BaseFixture { for (auto j = start_index; j < end_index; j++) { if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { count++; } } - ref_valid[i] = (count >= min_periods) and not(count == ddof); + ref_valid[i] = (count >= min_periods) and not(count <= ddof); if (ref_valid[i]) { OutputType mean{0}, m2{0}, tmp1, tmp2; From 1e1c8cd642075c37518cdcd07474b4d0ad703d9d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 26 Jul 2021 09:45:56 -0700 Subject: [PATCH 17/47] docfix --- cpp/include/cudf/aggregation.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/cpp/include/cudf/aggregation.hpp b/cpp/include/cudf/aggregation.hpp index 2296d2a4e69..6ede7555ce7 100644 --- a/cpp/include/cudf/aggregation.hpp +++ b/cpp/include/cudf/aggregation.hpp @@ -195,9 +195,6 @@ std::unique_ptr make_variance_aggregation(size_type ddof = 1); /** * @brief Factory to create a STD aggregation * - * In rolling window aggregation, when there is only one valid observation - * in the window, the result is null. - * * Chrono type is unsupported in rolling window STD aggregation. * * @param ddof Delta degrees of freedom. The divisor used in calculation of From 1288a567b79cc1317d1d526f5556d22f19dbd84d Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 26 Jul 2021 16:12:16 -0700 Subject: [PATCH 18/47] fixed_point fix --- cpp/src/rolling/rolling_detail.cuh | 8 +++- cpp/tests/rolling/rolling_test.cpp | 68 ++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index ac2a302291c..4d1667b7303 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -353,7 +353,13 @@ struct DeviceRollingVariance { m2_acc += tmp1 * tmp2; return thrust::make_tuple(r_count_acc, m_acc, m2_acc); }); - output.element(current_index) = m2 / (count - ddof); + if constexpr (is_fixed_point()) { + OutputType scaleby = exp10(static_cast(input.type().scale())); + scaleby *= scaleby; + output.element(current_index) = m2 / (count - ddof) * scaleby; + } else { + output.element(current_index) = m2 / (count - ddof); + } } return output_is_valid; diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index f1bc80d1e4b..370d0db127d 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -23,10 +23,13 @@ #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -381,6 +384,12 @@ class RollingTest : public cudf::test::BaseFixture { ref_data[i] = m2 / (count - ddof); if (do_square_root) { ref_data[i] = std::sqrt(ref_data[i]); } + + if (cudf::is_fixed_point()) { + OutputType scaleby{exp10(static_cast(input.type().scale()))}; + scaleby = scaleby * scaleby; + ref_data[i] *= scaleby; + } } } @@ -1358,6 +1367,65 @@ TYPED_TEST(FixedPointTests, MinMaxCountLagLeadNulls) CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_rowno, rowno->view()); } +TYPED_TEST(FixedPointTests, VarStd) +{ + using namespace numeric; + using namespace cudf; + using decimalXX = TypeParam; + using RepType = cudf::device_storage_type_t; + using fp_wrapper = cudf::test::fixed_point_column_wrapper; + using fw_wrapper = cudf::test::fixed_width_column_wrapper; + + size_type preceding_window{2}, following_window{0}, min_periods{1}, ddof{1}; + + std::vector result_base_v{-1, 1422984.5, 1401138.0, 1352.0, 2.0, 0.5}; + std::vector result_mask_v{0, 1, 1, 1, 1, 1}; + + // var tests + for (int32_t s = -5; s < 5; s++) { + auto const scale = scale_type{s}; + auto const input = fp_wrapper{{42, 1729, 55, 3, 1, 2}, {1, 1, 1, 1, 1, 1}, scale}; + + auto got = rolling_window( + input, + preceding_window, + following_window, + min_periods, + dynamic_cast(*cudf::make_variance_aggregation(ddof))); + + std::vector result_scaled_v(result_base_v.size()); + std::transform( + result_base_v.begin(), result_base_v.end(), result_scaled_v.begin(), [&s](auto x) { + return x * exp10(s) * exp10(s); + }); + fw_wrapper expect(result_scaled_v.begin(), result_scaled_v.end(), result_mask_v.begin()); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(expect, *got); + } + + // std tests + for (int32_t s = -5; s < 5; s++) { + auto const scale = scale_type{s}; + auto const input = fp_wrapper{{42, 1729, 55, 3, 1, 2}, {1, 1, 1, 1, 1, 1}, scale}; + + auto got = rolling_window( + input, + preceding_window, + following_window, + min_periods, + dynamic_cast(*cudf::make_std_aggregation(ddof))); + + std::vector result_scaled_v(result_base_v.size()); + std::transform( + result_base_v.begin(), result_base_v.end(), result_scaled_v.begin(), [&s](auto x) { + return std::sqrt(x * exp10(s) * exp10(s)); + }); + fw_wrapper expect(result_scaled_v.begin(), result_scaled_v.end(), result_mask_v.begin()); + + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(expect, *got); + } +} + class RollingDictionaryTest : public cudf::test::BaseFixture { }; From 3a9e589318618f8830edf33ea9f1ad2eef5d49b8 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 26 Jul 2021 16:18:48 -0700 Subject: [PATCH 19/47] docs --- cpp/src/rolling/rolling_detail.cuh | 2 ++ cpp/tests/rolling/rolling_test.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 4d1667b7303..e025a8b5b98 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -354,6 +354,8 @@ struct DeviceRollingVariance { return thrust::make_tuple(r_count_acc, m_acc, m2_acc); }); if constexpr (is_fixed_point()) { + // For fixed_point types, the previous computed value used unscaled rep-value, + // the final result should be multiplied by the square of decimal `scale`. OutputType scaleby = exp10(static_cast(input.type().scale())); scaleby *= scaleby; output.element(current_index) = m2 / (count - ddof) * scaleby; diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 370d0db127d..3f82361a2c9 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -1378,6 +1378,7 @@ TYPED_TEST(FixedPointTests, VarStd) size_type preceding_window{2}, following_window{0}, min_periods{1}, ddof{1}; + // The variance of `input` given `scale` == 0 std::vector result_base_v{-1, 1422984.5, 1401138.0, 1352.0, 2.0, 0.5}; std::vector result_mask_v{0, 1, 1, 1, 1, 1}; @@ -1396,6 +1397,7 @@ TYPED_TEST(FixedPointTests, VarStd) std::vector result_scaled_v(result_base_v.size()); std::transform( result_base_v.begin(), result_base_v.end(), result_scaled_v.begin(), [&s](auto x) { + // When values are scaled by 10^n, the variance is scaled by 10^2n. return x * exp10(s) * exp10(s); }); fw_wrapper expect(result_scaled_v.begin(), result_scaled_v.end(), result_mask_v.begin()); @@ -1418,6 +1420,7 @@ TYPED_TEST(FixedPointTests, VarStd) std::vector result_scaled_v(result_base_v.size()); std::transform( result_base_v.begin(), result_base_v.end(), result_scaled_v.begin(), [&s](auto x) { + // When values are scaled by 10^n, the variance is scaled by 10^2n. return std::sqrt(x * exp10(s) * exp10(s)); }); fw_wrapper expect(result_scaled_v.begin(), result_scaled_v.end(), result_mask_v.begin()); From 490539c0eef93d5b1614e166c3df7bb1148ff64f Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 26 Jul 2021 16:32:57 -0700 Subject: [PATCH 20/47] docfix --- cpp/include/cudf/aggregation.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cpp/include/cudf/aggregation.hpp b/cpp/include/cudf/aggregation.hpp index 6ede7555ce7..8d1246ff897 100644 --- a/cpp/include/cudf/aggregation.hpp +++ b/cpp/include/cudf/aggregation.hpp @@ -181,13 +181,10 @@ std::unique_ptr make_m2_aggregation(); /** * @brief Factory to create a VARIANCE aggregation * - * In rolling window aggregation, when there is only one valid observation - * in the window, the result is null. - * - * Chrono type is unsupported in rolling window VARIANCE aggregation. - * * @param ddof Delta degrees of freedom. The divisor used in calculation of * `variance` is `N - ddof`, where `N` is the population size. + * + * @throw cudf::logic_error if input type is chrono or compound types. */ template std::unique_ptr make_variance_aggregation(size_type ddof = 1); @@ -199,6 +196,8 @@ std::unique_ptr make_variance_aggregation(size_type ddof = 1); * * @param ddof Delta degrees of freedom. The divisor used in calculation of * `std` is `N - ddof`, where `N` is the population size. + * + * @throw cudf::logic_error if input type is chrono or compound types. */ template std::unique_ptr make_std_aggregation(size_type ddof = 1); From 394f0f003bc37acb9bddaa6b109a0fefd013a4ac Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 27 Jul 2021 09:59:16 -0700 Subject: [PATCH 21/47] docfix --- cpp/include/cudf/aggregation.hpp | 2 -- cpp/include/cudf/rolling.hpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp/include/cudf/aggregation.hpp b/cpp/include/cudf/aggregation.hpp index 8d1246ff897..c6791bead63 100644 --- a/cpp/include/cudf/aggregation.hpp +++ b/cpp/include/cudf/aggregation.hpp @@ -192,8 +192,6 @@ std::unique_ptr make_variance_aggregation(size_type ddof = 1); /** * @brief Factory to create a STD aggregation * - * Chrono type is unsupported in rolling window STD aggregation. - * * @param ddof Delta degrees of freedom. The divisor used in calculation of * `std` is `N - ddof`, where `N` is the population size. * diff --git a/cpp/include/cudf/rolling.hpp b/cpp/include/cudf/rolling.hpp index 4373e103f3b..21df994c644 100644 --- a/cpp/include/cudf/rolling.hpp +++ b/cpp/include/cudf/rolling.hpp @@ -43,7 +43,7 @@ namespace cudf { * * Notes on return column types: * - The returned column for count aggregation always has `INT32` type. - * - The returned column for VARIANCE/STD aggregations always has `double` type. + * - The returned column for VARIANCE/STD aggregations always has `FLOAT64` type. * - All other operators return a column of the same type as the input. Therefore * it is suggested to convert integer column types (especially low-precision integers) * to `FLOAT32` or `FLOAT64` before doing a rolling `MEAN`. From fe2b4f4e014f62b2465cbf9f9dcc11b6d8f78e34 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 27 Jul 2021 12:29:28 -0700 Subject: [PATCH 22/47] initial testings --- python/cudf/cudf/testing/dataset_generator.py | 13 ++++-- python/cudf/cudf/tests/test_rolling.py | 40 +++++++++++++------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/python/cudf/cudf/testing/dataset_generator.py b/python/cudf/cudf/testing/dataset_generator.py index 600600c16f1..96d02c2e6c1 100644 --- a/python/cudf/cudf/testing/dataset_generator.py +++ b/python/cudf/cudf/testing/dataset_generator.py @@ -488,8 +488,11 @@ def int_generator(dtype, size): Generator for int data """ iinfo = np.iinfo(dtype) + # return lambda: np.random.randint( + # low=iinfo.min, high=iinfo.max, size=size, dtype=dtype, + # ) return lambda: np.random.randint( - low=iinfo.min, high=iinfo.max, size=size, dtype=dtype, + low=-100, high=100, size=size, dtype=dtype, ) @@ -499,9 +502,11 @@ def float_generator(dtype, size): """ finfo = np.finfo(dtype) return ( - lambda: np.random.uniform( - low=finfo.min / 2, high=finfo.max / 2, size=size, - ) + # lambda: np.random.uniform( + # low=finfo.min / 2, high=finfo.max / 2, size=size, + # ) + # * 2 + lambda: np.random.uniform(low=-100, high=100, size=size,) * 2 ) diff --git a/python/cudf/cudf/tests/test_rolling.py b/python/cudf/cudf/tests/test_rolling.py index ad2cd7e6692..34825ea1013 100644 --- a/python/cudf/cudf/tests/test_rolling.py +++ b/python/cudf/cudf/tests/test_rolling.py @@ -133,7 +133,12 @@ def test_rolling_with_offset(agg): @pytest.mark.parametrize("agg", ["std", "var"]) @pytest.mark.parametrize("center", [True, False]) -def test_rolling_var_std_dynamic(agg, center): +@pytest.mark.parametrize("seed", [100, 1000, 20000]) +@pytest.mark.parametrize("window_size", [2, 10, 100, 1000]) +@pytest.mark.parametrize( + "min_periods", [1] +) # min_periods is tested with previous tests +def test_rolling_var_std_dynamic(agg, center, seed, window_size, min_periods): if PANDAS_GE_110: kwargs = {"check_freq": False} else: @@ -149,23 +154,32 @@ def test_rolling_var_std_dynamic(agg, center): ], rows=n_rows, use_threads=False, + seed=seed, ) pdf = data.to_pandas() - pdf["1"][ - [np.random.randint(0, n_rows - 1) for _ in range(int(n_rows * 0.2))] - ] = float("inf") + # pdf["1"][ + # [np.random.randint(0, n_rows - 1) for _ in range(int(n_rows * 0.2))] + # ] = float("inf") gdf = cudf.from_pandas(pdf) - for window_size in range(1, len(data) + 1): - for min_periods in range(1, window_size + 1): - expect = getattr( - pdf.rolling(window_size, min_periods, center), agg - )().fillna(-1) - got = getattr( - gdf.rolling(window_size, min_periods, center), agg - )().fillna(-1) - assert_eq(expect, got, **kwargs) + + expect = getattr( + pdf.rolling(window_size, min_periods, center), agg + )().fillna(-1) + got = getattr(gdf.rolling(window_size, min_periods, center), agg)().fillna( + -1 + ) + + # Pandas adopts Kahan summation, where there's a running compensation term + # from a previous window rolled into the next. This makes the variation of + # a uniform window non-zero. In cudf, each window is computed independently + # of the previous window. Thus, here we skip comparing the rows where cudf + # computes a 0-variance for the window. + for col in expect: + expect[col][got[col][got[col] == 0.0].index.to_pandas()] = 0.0 + + assert_eq(expect, got, **kwargs) def test_rolling_count_with_offset(): From bc0920b40a123f9feada9ee138fefd855e9a1174 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 27 Jul 2021 14:24:30 -0700 Subject: [PATCH 23/47] remove thrust::reduce --- cpp/src/rolling/rolling_detail.cuh | 40 ++++++++++++------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index e025a8b5b98..1f58b014839 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -328,31 +328,23 @@ struct DeviceRollingVariance { bool output_is_valid = (count >= min_periods) and not(count <= ddof); if (output_is_valid) { - // Welford algorithm, a numerically stable, single pass algorithm + // Welford algorithm // See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance - OutputType m, m2; - size_type running_count; - - thrust::tie(running_count, m, m2) = - thrust::reduce(thrust::seq, - thrust::make_counting_iterator(start_index), - thrust::make_counting_iterator(end_index), - thrust::make_tuple(size_type{0}, OutputType{0}, OutputType{0}), - [&](auto acc, auto i) { - if (has_nulls and input.is_null_nocheck(i)) { return acc; } - - OutputType m_acc, m2_acc, tmp1, tmp2; - size_type r_count_acc; - thrust::tie(r_count_acc, m_acc, m2_acc) = acc; - OutputType x = static_cast(input.element(i)); - - r_count_acc++; - tmp1 = x - m_acc; - m_acc += tmp1 / r_count_acc; - tmp2 = x - m_acc; - m2_acc += tmp1 * tmp2; - return thrust::make_tuple(r_count_acc, m_acc, m2_acc); - }); + OutputType m{0}, m2(0); + size_type running_count{0}; + + for (size_type i = start_index; i < end_index; i++) { + if (has_nulls and input.is_null_nocheck(i)) { continue; } + + OutputType tmp1, tmp2; + OutputType x = static_cast(input.element(i)); + + running_count++; + tmp1 = x - m; + m += tmp1 / running_count; + tmp2 = x - m; + m2 += tmp1 * tmp2; + } if constexpr (is_fixed_point()) { // For fixed_point types, the previous computed value used unscaled rep-value, // the final result should be multiplied by the square of decimal `scale`. From 934f1047739065410d674b463f4b84da963d8163 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 27 Jul 2021 14:36:36 -0700 Subject: [PATCH 24/47] . --- cpp/src/rolling/rolling_detail.cuh | 5 ++--- cpp/tests/rolling/rolling_test.cpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 1f58b014839..51d5ccbb9ba 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -330,14 +330,13 @@ struct DeviceRollingVariance { if (output_is_valid) { // Welford algorithm // See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance - OutputType m{0}, m2(0); + OutputType m{0}, m2(0), tmp1, tmp2; size_type running_count{0}; for (size_type i = start_index; i < end_index; i++) { if (has_nulls and input.is_null_nocheck(i)) { continue; } - OutputType tmp1, tmp2; - OutputType x = static_cast(input.element(i)); + OutputType const& x = static_cast(input.element(i)); running_count++; tmp1 = x - m; diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 3f82361a2c9..f2012bfa86e 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -361,7 +361,7 @@ class RollingTest : public cudf::test::BaseFixture { size_type start_index = std::min(start, end); size_type end_index = std::max(start, end); - // compute window var/std - with raw loop, alternative to implementation + // compute window var/std size_type count{0}; // valid count in window for (auto j = start_index; j < end_index; j++) { if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { count++; } From b21519965abab4d0295adde087a557a28376564a Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 28 Jul 2021 18:45:23 -0700 Subject: [PATCH 25/47] Update cpp/src/rolling/rolling_detail.cuh Co-authored-by: Mark Harris --- cpp/src/rolling/rolling_detail.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 51d5ccbb9ba..2651ac3a7f6 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -325,7 +325,7 @@ struct DeviceRollingVariance { } // Variance/Std is non-negative, thus ddof should be strictly less than valid counts. - bool output_is_valid = (count >= min_periods) and not(count <= ddof); + bool output_is_valid = (count >= min_periods) and (ddof < count); if (output_is_valid) { // Welford algorithm From a3af3e925a62fcdc71f147822494162991b2cf89 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 28 Jul 2021 18:56:53 -0700 Subject: [PATCH 26/47] Update cpp/src/rolling/rolling_detail.cuh Co-authored-by: David Wendt <45795991+davidwendt@users.noreply.github.com> --- cpp/src/rolling/rolling_detail.cuh | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 2651ac3a7f6..9ebdff36744 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -312,17 +312,12 @@ struct DeviceRollingVariance { { using DeviceInputType = device_storage_type_t; - cudf::size_type count{0}; // valid counts in the window - - // Count valid observations in window - if (has_nulls) { - count = thrust::count_if(thrust::seq, + // valid counts in the window + cudf::size_type const count = has_nulls ? thrust::count_if(thrust::seq, thrust::make_counting_iterator(start_index), thrust::make_counting_iterator(end_index), - [&input](auto i) { return input.is_valid_nocheck(i); }); - } else { - count = end_index - start_index; - } + [&input](auto i) { return input.is_valid_nocheck(i); }) + : end_index - start_index; // Variance/Std is non-negative, thus ddof should be strictly less than valid counts. bool output_is_valid = (count >= min_periods) and (ddof < count); From ae33de0dc13517061f8cf46f956a11bc377f20b8 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 28 Jul 2021 18:58:36 -0700 Subject: [PATCH 27/47] Update cpp/src/rolling/rolling_detail.cuh Co-authored-by: David Wendt <45795991+davidwendt@users.noreply.github.com> --- cpp/src/rolling/rolling_detail.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 9ebdff36744..b2359cccb1b 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -331,7 +331,7 @@ struct DeviceRollingVariance { for (size_type i = start_index; i < end_index; i++) { if (has_nulls and input.is_null_nocheck(i)) { continue; } - OutputType const& x = static_cast(input.element(i)); + OutputType const x = static_cast(input.element(i)); running_count++; tmp1 = x - m; From 645a17275be3ba7a6c9cfffd81807c9f2a0542b1 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 28 Jul 2021 19:08:14 -0700 Subject: [PATCH 28/47] address review comments --- cpp/src/rolling/rolling_detail.cuh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index b2359cccb1b..39d1cda9c8b 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -313,11 +313,12 @@ struct DeviceRollingVariance { using DeviceInputType = device_storage_type_t; // valid counts in the window - cudf::size_type const count = has_nulls ? thrust::count_if(thrust::seq, - thrust::make_counting_iterator(start_index), - thrust::make_counting_iterator(end_index), - [&input](auto i) { return input.is_valid_nocheck(i); }) - : end_index - start_index; + cudf::size_type const count = + has_nulls ? thrust::count_if(thrust::seq, + thrust::make_counting_iterator(start_index), + thrust::make_counting_iterator(end_index), + [&input](auto i) { return input.is_valid_nocheck(i); }) + : end_index - start_index; // Variance/Std is non-negative, thus ddof should be strictly less than valid counts. bool output_is_valid = (count >= min_periods) and (ddof < count); @@ -325,12 +326,13 @@ struct DeviceRollingVariance { if (output_is_valid) { // Welford algorithm // See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance - OutputType m{0}, m2(0), tmp1, tmp2; + OutputType m{0}, m2(0); size_type running_count{0}; for (size_type i = start_index; i < end_index; i++) { if (has_nulls and input.is_null_nocheck(i)) { continue; } + OutputType tmp1, tmp2; OutputType const x = static_cast(input.element(i)); running_count++; From d94b8db80f58e8cd484628236e3b87d18be0c96a Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 29 Jul 2021 10:16:25 -0700 Subject: [PATCH 29/47] Apply suggestions from code review Co-authored-by: David Wendt <45795991+davidwendt@users.noreply.github.com> --- cpp/src/rolling/rolling_detail.cuh | 7 +++---- cpp/tests/rolling/rolling_test.cpp | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 39d1cda9c8b..3a08badf0b2 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -326,19 +326,18 @@ struct DeviceRollingVariance { if (output_is_valid) { // Welford algorithm // See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance - OutputType m{0}, m2(0); + OutputType m{0}, m2{0}; size_type running_count{0}; for (size_type i = start_index; i < end_index; i++) { if (has_nulls and input.is_null_nocheck(i)) { continue; } - OutputType tmp1, tmp2; OutputType const x = static_cast(input.element(i)); running_count++; - tmp1 = x - m; + OutputType const tmp1 = x - m; m += tmp1 / running_count; - tmp2 = x - m; + OutputType const tmp2 = x - m; m2 += tmp1 * tmp2; } if constexpr (is_fixed_point()) { diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index f2012bfa86e..56880cad0b5 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -369,15 +369,15 @@ class RollingTest : public cudf::test::BaseFixture { ref_valid[i] = (count >= min_periods) and not(count <= ddof); if (ref_valid[i]) { - OutputType mean{0}, m2{0}, tmp1, tmp2; + OutputType mean{0}, m2{0}; size_type r_count{0}; for (auto j = start_index; j < end_index; j++) { if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { r_count++; - tmp1 = in_col[j] - mean; + OutputType const tmp1 = in_col[j] - mean; mean += tmp1 / r_count; - tmp2 = in_col[j] - mean; + OutputType const tmp2 = in_col[j] - mean; m2 += tmp1 * tmp2; } } From 0fe4a87c25118cdd1a67ab73a0bfde96a4763cd5 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 29 Jul 2021 17:39:02 -0700 Subject: [PATCH 30/47] Add nan tests --- cpp/tests/rolling/rolling_test.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 56880cad0b5..22f24d8e64c 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -766,8 +766,8 @@ TYPED_TEST(RollingVarStdTest, SimpleStaticVarianceStd) auto const expected_var = cudf::is_boolean() - ? std::vector{XXX, XXX, 0, 0, XXX, XXX, 0.5, 0.33333333333333337, 0, 0} - : std::vector{XXX, XXX, 8, 8, XXX, XXX, 32, 16.333333333333332, 3, 4.5}; + ? std::vector{XXX, XXX, 0, 0, XXX, XXX, 0.5, 0.3333333333333333, 0, 0} + : std::vector{XXX, XXX, 8, 8, XXX, XXX, 32, 16.33333333333333, 3, 4.5}; std::vector expected_std(expected_var.size()); std::transform(expected_var.begin(), expected_var.end(), expected_std.begin(), [](auto const& x) { return std::sqrt(x); @@ -802,7 +802,7 @@ TYPED_TEST(RollingVarStdTest, SimpleStaticVarianceStd) #undef XXX } -TEST_F(RollingtVarStdTestUntyped, SimpleStaticVarianceStdInf) +TEST_F(RollingtVarStdTestUntyped, SimpleStaticVarianceStdInfNaN) { #define XXX 0. // NULL stub @@ -813,16 +813,17 @@ TEST_F(RollingtVarStdTestUntyped, SimpleStaticVarianceStdInf) size_type const ddof = 1, min_periods = 1, preceding_window = 3, following_window = 0; auto const col_data = - cudf::test::make_type_param_vector({5., 4., XXX, inf, 4., 8., 0., XXX, XXX, 5.}); - const std::vector col_mask = {1, 1, 0, 1, 1, 1, 1, 0, 0, 1}; + cudf::test::make_type_param_vector({5., 4., XXX, inf, 4., 8., 0., nan, XXX, 5.}); + const std::vector col_mask = {1, 1, 0, 1, 1, 1, 1, 1, 0, 1}; - auto const expected_var = std::vector{XXX, 0.5, 0.5, nan, nan, nan, 16, 32, XXX, XXX}; + auto const expected_var = + std::vector{XXX, 0.5, 0.5, nan, nan, nan, 16, nan, nan, nan}; std::vector expected_std(expected_var.size()); std::transform(expected_var.begin(), expected_var.end(), expected_std.begin(), [](auto const& x) { return std::sqrt(x); }); - const std::vector expected_mask = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0}; + const std::vector expected_mask = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}; fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); fixed_width_column_wrapper var_expect( From 9ce41ae2a803103ce12395b28840c6752228d219 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 29 Jul 2021 18:08:09 -0700 Subject: [PATCH 31/47] Remove auto generated column test --- cpp/tests/rolling/rolling_test.cpp | 118 ----------------------------- 1 file changed, 118 deletions(-) diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 22f24d8e64c..26fa39961ed 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -257,27 +257,6 @@ class RollingTest : public cudf::test::BaseFixture { min_periods, *cudf::make_mean_aggregation()); } - - if (cudf::is_fixed_width(input.type()) and not cudf::is_chrono(input.type())) { - size_type min_window_size = preceding_window[0] + following_window[0]; - for (size_t i = 0; i < preceding_window.size(); i++) { - min_window_size = std::min(min_window_size, preceding_window[i] + following_window[i]); - } - - for (size_type ddof = 0; ddof < min_window_size; ddof *= 2) { - run_test_col(input, - preceding_window, - following_window, - min_periods, - *cudf::make_variance_aggregation(ddof)); - run_test_col(input, - preceding_window, - following_window, - min_periods, - *cudf::make_std_aggregation(ddof)); - if (ddof == 0) { ddof++; } - } - } } private: @@ -327,89 +306,6 @@ class RollingTest : public cudf::test::BaseFixture { return col.release(); } - template () and not cudf::is_chrono())> - std::unique_ptr create_var_std_reference_output( - cudf::column_view const& input, - std::vector const& preceding_window_col, - std::vector const& following_window_col, - size_type min_periods, - size_type ddof) - { - using OutputType = double; - - size_type num_rows = input.size(); - std::vector ref_data(num_rows); - std::vector ref_valid(num_rows); - - // input data and mask - thrust::host_vector in_col; - std::vector in_valid; - std::tie(in_col, in_valid) = cudf::test::to_host(input); - bitmask_type* valid_mask = in_valid.data(); - - for (size_type i = 0; i < num_rows; i++) { - // load sizes - min_periods = std::max(min_periods, 1); // at least one observation is required - - // compute bounds - auto preceding_window = preceding_window_col[i % preceding_window_col.size()]; - auto following_window = following_window_col[i % following_window_col.size()]; - size_type start = std::min(num_rows, std::max(0, i - preceding_window + 1)); - size_type end = std::min(num_rows, std::max(0, i + following_window + 1)); - size_type start_index = std::min(start, end); - size_type end_index = std::max(start, end); - - // compute window var/std - size_type count{0}; // valid count in window - for (auto j = start_index; j < end_index; j++) { - if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { count++; } - } - ref_valid[i] = (count >= min_periods) and not(count <= ddof); - - if (ref_valid[i]) { - OutputType mean{0}, m2{0}; - size_type r_count{0}; - - for (auto j = start_index; j < end_index; j++) { - if (not input.nullable() or cudf::bit_is_set(valid_mask, j)) { - r_count++; - OutputType const tmp1 = in_col[j] - mean; - mean += tmp1 / r_count; - OutputType const tmp2 = in_col[j] - mean; - m2 += tmp1 * tmp2; - } - } - - ref_data[i] = m2 / (count - ddof); - if (do_square_root) { ref_data[i] = std::sqrt(ref_data[i]); } - - if (cudf::is_fixed_point()) { - OutputType scaleby{exp10(static_cast(input.type().scale()))}; - scaleby = scaleby * scaleby; - ref_data[i] *= scaleby; - } - } - } - - fixed_width_column_wrapper col(ref_data.begin(), ref_data.end(), ref_valid.begin()); - return col.release(); - } - - template () or cudf::is_chrono())> - std::unique_ptr create_var_std_reference_output( - cudf::column_view const& input, - std::vector const& preceding_window_col, - std::vector const& following_window_col, - size_type min_periods, - size_type ddof) - { - CUDF_FAIL("Unsupported combination of type and aggregation"); - } - template , true>( input, preceding_window, following_window, min_periods); - case cudf::aggregation::VARIANCE: - return create_var_std_reference_output( - input, - preceding_window, - following_window, - min_periods, - dynamic_cast(&op)->_ddof); - case cudf::aggregation::STD: - return create_var_std_reference_output( - input, - preceding_window, - following_window, - min_periods, - dynamic_cast(&op)->_ddof); default: return fixed_width_column_wrapper({}).release(); } } From 43df716951da8fdfe0d057244753b88db6557509 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Fri, 20 Aug 2021 18:13:25 -0700 Subject: [PATCH 32/47] count==0 case maps to invalid output. --- cpp/src/rolling/rolling_detail.cuh | 5 +- cpp/tests/rolling/rolling_test.cpp | 93 ++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 46ac40d171a..a5588b7fcc0 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -320,8 +320,9 @@ struct DeviceRollingVariance { [&input](auto i) { return input.is_valid_nocheck(i); }) : end_index - start_index; - // Variance/Std is non-negative, thus ddof should be strictly less than valid counts. - bool output_is_valid = (count >= min_periods) and (ddof < count); + // The denominator of the variance is `count - ddof`, it is strictly positive + // to gaurantee that variance is non-negative. + bool output_is_valid = count > 0 and (count >= min_periods) and (ddof < count); if (output_is_valid) { // Welford algorithm diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index ec2ab69e048..b6de97a4e02 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -623,22 +623,103 @@ TYPED_TEST(RollingVarStdTest, SimpleStaticVarianceStd) using ResultType = double; - size_type const ddof = 1, min_periods = 1, preceding_window = 2, following_window = 1; + size_type const ddof = 1, min_periods = 0, preceding_window = 2, following_window = 1; auto const col_data = - cudf::test::make_type_param_vector({XXX, XXX, 9, 5, XXX, XXX, 0, 8, 5, 8}); - const std::vector col_mask = {0, 0, 1, 1, 0, 0, 1, 1, 1, 1}; + cudf::test::make_type_param_vector({XXX, XXX, 9, 5, XXX, XXX, XXX, 0, 8, 5, 8}); + const std::vector col_mask = {0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1}; auto const expected_var = cudf::is_boolean() - ? std::vector{XXX, XXX, 0, 0, XXX, XXX, 0.5, 0.3333333333333333, 0, 0} - : std::vector{XXX, XXX, 8, 8, XXX, XXX, 32, 16.33333333333333, 3, 4.5}; + ? std::vector{XXX, XXX, 0, 0, XXX, XXX, XXX, 0.5, 0.3333333333333333, 0, 0} + : std::vector{XXX, XXX, 8, 8, XXX, XXX, XXX, 32, 16.33333333333333, 3, 4.5}; std::vector expected_std(expected_var.size()); std::transform(expected_var.begin(), expected_var.end(), expected_std.begin(), [](auto const& x) { return std::sqrt(x); }); - const std::vector expected_mask = {0, 0, 1, 1, 0, 0, 1, 1, 1, 1}; + const std::vector expected_mask = {0, /* all null window */ + 0, /* count == ddof */ + 1, + 1, + 0, /* count == ddof */ + 0, /* all null window */ + 0, /* count == ddof */ + 1, + 1, + 1, + 1}; + + fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); + fixed_width_column_wrapper var_expect( + expected_var.begin(), expected_var.end(), expected_mask.begin()); + fixed_width_column_wrapper std_expect( + expected_std.begin(), expected_std.end(), expected_mask.begin()); + + std::unique_ptr var_result, std_result; + // static sizes + EXPECT_NO_THROW(var_result = cudf::rolling_window(input, + preceding_window, + following_window, + min_periods, + dynamic_cast( + *cudf::make_variance_aggregation(ddof)));); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*var_result, var_expect); + + EXPECT_NO_THROW(std_result = cudf::rolling_window(input, + preceding_window, + following_window, + min_periods, + dynamic_cast( + *cudf::make_std_aggregation(ddof)));); + CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*std_result, std_expect); + +#undef XXX +} + +TYPED_TEST(RollingVarStdTest, SimpleStaticVarianceStdNegativeDDOF) +{ +#define XXX 0 // NULL stub + + using ResultType = double; + + size_type const ddof = -1, min_periods = 0, preceding_window = 2, following_window = 1; + + auto const col_data = + cudf::test::make_type_param_vector({XXX, XXX, 9, 5, XXX, XXX, XXX, 0, 8, 5, 8}); + const std::vector col_mask = {0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1}; + + auto const expected_var = + cudf::is_boolean() + ? std::vector< + ResultType>{XXX, 0, 0, 0, 0, XXX, 0, 0.16666666666666667, 0.1666666666666667, 0, 0} + : std::vector{XXX, + 0, + 2.666666666666667, + 2.666666666666667, + 0, + XXX, + 0, + 10.666666666666667, + 8.166666666666667, + 1.5, + 1.5}; + std::vector expected_std(expected_var.size()); + std::transform(expected_var.begin(), expected_var.end(), expected_std.begin(), [](auto const& x) { + return std::sqrt(x); + }); + + const std::vector expected_mask = {0, /* all null window */ + 1, + 1, + 1, + 1, + 0, /* all null window */ + 1, + 1, + 1, + 1, + 1}; fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); fixed_width_column_wrapper var_expect( From 652020f9d6dc28ad90cb8e164c90863000a9bdb9 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 23 Aug 2021 15:28:23 -0700 Subject: [PATCH 33/47] Detail docs over pandas vs cudf difference on uniform windows --- python/cudf/cudf/tests/test_rolling.py | 45 ++++++++++++-------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/python/cudf/cudf/tests/test_rolling.py b/python/cudf/cudf/tests/test_rolling.py index 34825ea1013..4445ac09389 100644 --- a/python/cudf/cudf/tests/test_rolling.py +++ b/python/cudf/cudf/tests/test_rolling.py @@ -133,18 +133,15 @@ def test_rolling_with_offset(agg): @pytest.mark.parametrize("agg", ["std", "var"]) @pytest.mark.parametrize("center", [True, False]) -@pytest.mark.parametrize("seed", [100, 1000, 20000]) +@pytest.mark.parametrize("seed", [100, 1000, 10000]) @pytest.mark.parametrize("window_size", [2, 10, 100, 1000]) -@pytest.mark.parametrize( - "min_periods", [1] -) # min_periods is tested with previous tests -def test_rolling_var_std_dynamic(agg, center, seed, window_size, min_periods): +def test_rolling_var_std_large(agg, center, seed, window_size): if PANDAS_GE_110: kwargs = {"check_freq": False} else: kwargs = {} - n_rows = 10_000 + n_rows = 1_000 data = dataset_generator.rand_dataframe( dtypes_meta=[ {"dtype": "i4", "null_frequency": 0.4, "cardinality": 100}, @@ -158,26 +155,26 @@ def test_rolling_var_std_dynamic(agg, center, seed, window_size, min_periods): ) pdf = data.to_pandas() - # pdf["1"][ - # [np.random.randint(0, n_rows - 1) for _ in range(int(n_rows * 0.2))] - # ] = float("inf") - gdf = cudf.from_pandas(pdf) - expect = getattr( - pdf.rolling(window_size, min_periods, center), agg - )().fillna(-1) - got = getattr(gdf.rolling(window_size, min_periods, center), agg)().fillna( - -1 - ) - - # Pandas adopts Kahan summation, where there's a running compensation term - # from a previous window rolled into the next. This makes the variation of - # a uniform window non-zero. In cudf, each window is computed independently - # of the previous window. Thus, here we skip comparing the rows where cudf - # computes a 0-variance for the window. - for col in expect: - expect[col][got[col][got[col] == 0.0].index.to_pandas()] = 0.0 + expect = getattr(pdf.rolling(window_size, 1, center), agg)().fillna(-1) + got = getattr(gdf.rolling(window_size, 1, center), agg)().fillna(-1) + + # Pandas adopts an online variance calculation algorithm. Each window has a + # numeric error from the previous window. This makes the variance of a + # uniform window has a small residue. Taking the square root of a very + # small number may result in a non-trival number. + # + # In cudf, each window is computed independently from the previous window, + # this gives better numeric precision. + # + # For quantitative analysis: + # https://gist.github.com/isVoid/4984552da6ef5545348399c22d72cffb + # + # To make up this difference, we skip comparing uniform windows by coercing + # pandas result of these windows to 0. + for col in ["1", "2", "3"]: + expect[col][(got[col] == 0.0).to_pandas()] = 0.0 assert_eq(expect, got, **kwargs) From d62eb00b6a0814f7d0b49be9efc91c761638a688 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 24 Aug 2021 17:08:57 -0700 Subject: [PATCH 34/47] Apply review comments: div by zero result is valid element --- cpp/src/rolling/rolling_detail.cuh | 9 ++- cpp/tests/rolling/rolling_test.cpp | 97 +++++------------------------- 2 files changed, 21 insertions(+), 85 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index a5588b7fcc0..683f2354373 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -320,9 +320,12 @@ struct DeviceRollingVariance { [&input](auto i) { return input.is_valid_nocheck(i); }) : end_index - start_index; - // The denominator of the variance is `count - ddof`, it is strictly positive - // to gaurantee that variance is non-negative. - bool output_is_valid = count > 0 and (count >= min_periods) and (ddof < count); + // Result is null in one of the following cases: + // - All inputs are null + // - Number of valid inputs is less than `min_periods` + // - Result is negative + // When `ddof == count`, the result is valid with div by zero values (inf or nan) + bool output_is_valid = count > 0 and (count >= min_periods) and (ddof <= count); if (output_is_valid) { // Welford algorithm diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index b6de97a4e02..f020c214804 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -623,67 +623,9 @@ TYPED_TEST(RollingVarStdTest, SimpleStaticVarianceStd) using ResultType = double; - size_type const ddof = 1, min_periods = 0, preceding_window = 2, following_window = 1; - - auto const col_data = - cudf::test::make_type_param_vector({XXX, XXX, 9, 5, XXX, XXX, XXX, 0, 8, 5, 8}); - const std::vector col_mask = {0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1}; + double const nan = std::numeric_limits::signaling_NaN(); - auto const expected_var = - cudf::is_boolean() - ? std::vector{XXX, XXX, 0, 0, XXX, XXX, XXX, 0.5, 0.3333333333333333, 0, 0} - : std::vector{XXX, XXX, 8, 8, XXX, XXX, XXX, 32, 16.33333333333333, 3, 4.5}; - std::vector expected_std(expected_var.size()); - std::transform(expected_var.begin(), expected_var.end(), expected_std.begin(), [](auto const& x) { - return std::sqrt(x); - }); - - const std::vector expected_mask = {0, /* all null window */ - 0, /* count == ddof */ - 1, - 1, - 0, /* count == ddof */ - 0, /* all null window */ - 0, /* count == ddof */ - 1, - 1, - 1, - 1}; - - fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); - fixed_width_column_wrapper var_expect( - expected_var.begin(), expected_var.end(), expected_mask.begin()); - fixed_width_column_wrapper std_expect( - expected_std.begin(), expected_std.end(), expected_mask.begin()); - - std::unique_ptr var_result, std_result; - // static sizes - EXPECT_NO_THROW(var_result = cudf::rolling_window(input, - preceding_window, - following_window, - min_periods, - dynamic_cast( - *cudf::make_variance_aggregation(ddof)));); - CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*var_result, var_expect); - - EXPECT_NO_THROW(std_result = cudf::rolling_window(input, - preceding_window, - following_window, - min_periods, - dynamic_cast( - *cudf::make_std_aggregation(ddof)));); - CUDF_TEST_EXPECT_COLUMNS_EQUIVALENT(*std_result, std_expect); - -#undef XXX -} - -TYPED_TEST(RollingVarStdTest, SimpleStaticVarianceStdNegativeDDOF) -{ -#define XXX 0 // NULL stub - - using ResultType = double; - - size_type const ddof = -1, min_periods = 0, preceding_window = 2, following_window = 1; + size_type const ddof = 1, min_periods = 0, preceding_window = 2, following_window = 1; auto const col_data = cudf::test::make_type_param_vector({XXX, XXX, 9, 5, XXX, XXX, XXX, 0, 8, 5, 8}); @@ -691,31 +633,20 @@ TYPED_TEST(RollingVarStdTest, SimpleStaticVarianceStdNegativeDDOF) auto const expected_var = cudf::is_boolean() - ? std::vector< - ResultType>{XXX, 0, 0, 0, 0, XXX, 0, 0.16666666666666667, 0.1666666666666667, 0, 0} - : std::vector{XXX, - 0, - 2.666666666666667, - 2.666666666666667, - 0, - XXX, - 0, - 10.666666666666667, - 8.166666666666667, - 1.5, - 1.5}; + ? std::vector{XXX, nan, 0, 0, nan, XXX, nan, 0.5, 0.3333333333333333, 0, 0} + : std::vector{XXX, nan, 8, 8, nan, XXX, nan, 32, 16.33333333333333, 3, 4.5}; std::vector expected_std(expected_var.size()); std::transform(expected_var.begin(), expected_var.end(), expected_std.begin(), [](auto const& x) { return std::sqrt(x); }); const std::vector expected_mask = {0, /* all null window */ + 1, /* 0 div 0, nan */ 1, 1, - 1, - 1, + 1, /* 0 div 0, nan */ 0, /* all null window */ - 1, + 1, /* 0 div 0, nan */ 1, 1, 1, @@ -763,13 +694,13 @@ TEST_F(RollingtVarStdTestUntyped, SimpleStaticVarianceStdInfNaN) const std::vector col_mask = {1, 1, 0, 1, 1, 1, 1, 1, 0, 1}; auto const expected_var = - std::vector{XXX, 0.5, 0.5, nan, nan, nan, 16, nan, nan, nan}; + std::vector{nan, 0.5, 0.5, nan, nan, nan, 16, nan, nan, nan}; std::vector expected_std(expected_var.size()); std::transform(expected_var.begin(), expected_var.end(), expected_std.begin(), [](auto const& x) { return std::sqrt(x); }); - const std::vector expected_mask = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + const std::vector expected_mask = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; fixed_width_column_wrapper input(col_data.begin(), col_data.end(), col_mask.begin()); fixed_width_column_wrapper var_expect( @@ -1323,14 +1254,16 @@ TYPED_TEST(FixedPointTests, VarStd) using fp_wrapper = cudf::test::fixed_point_column_wrapper; using fw_wrapper = cudf::test::fixed_width_column_wrapper; - size_type preceding_window{2}, following_window{0}, min_periods{1}, ddof{1}; + double const inf = std::numeric_limits::infinity(); + size_type preceding_window{3}, following_window{0}, min_periods{1}, ddof{2}; // The variance of `input` given `scale` == 0 - std::vector result_base_v{-1, 1422984.5, 1401138.0, 1352.0, 2.0, 0.5}; + std::vector result_base_v{ + -1, inf, 1882804.66666666667, 1928018.666666666667, 1874.6666666666667, 2.0}; std::vector result_mask_v{0, 1, 1, 1, 1, 1}; // var tests - for (int32_t s = -5; s < 5; s++) { + for (int32_t s = -2; s <= 2; s++) { auto const scale = scale_type{s}; auto const input = fp_wrapper{{42, 1729, 55, 3, 1, 2}, {1, 1, 1, 1, 1, 1}, scale}; @@ -1353,7 +1286,7 @@ TYPED_TEST(FixedPointTests, VarStd) } // std tests - for (int32_t s = -5; s < 5; s++) { + for (int32_t s = -2; s <= 2; s++) { auto const scale = scale_type{s}; auto const input = fp_wrapper{{42, 1729, 55, 3, 1, 2}, {1, 1, 1, 1, 1, 1}, scale}; From 0b78ab3ad587698fc1f976e85c8eef0144fd07a7 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 25 Aug 2021 10:50:58 -0700 Subject: [PATCH 35/47] Update cpp/src/rolling/rolling_detail.cuh Co-authored-by: Christopher Harris --- cpp/src/rolling/rolling_detail.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 683f2354373..243488fd2bf 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -320,7 +320,7 @@ struct DeviceRollingVariance { [&input](auto i) { return input.is_valid_nocheck(i); }) : end_index - start_index; - // Result is null in one of the following cases: + // Result will be null if any of the following conditions are met: // - All inputs are null // - Number of valid inputs is less than `min_periods` // - Result is negative From e3f89df23c76e3626d686f36266912aeac840cd1 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Mon, 30 Aug 2021 16:43:35 -0700 Subject: [PATCH 36/47] ddof > count situation is valid but nan --- cpp/src/rolling/rolling_detail.cuh | 57 ++++++++++++++++-------------- cpp/tests/rolling/rolling_test.cpp | 5 +-- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index 683f2354373..4c48b444951 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -65,6 +65,8 @@ #include #include +#include + #include namespace cudf { @@ -323,35 +325,38 @@ struct DeviceRollingVariance { // Result is null in one of the following cases: // - All inputs are null // - Number of valid inputs is less than `min_periods` - // - Result is negative - // When `ddof == count`, the result is valid with div by zero values (inf or nan) - bool output_is_valid = count > 0 and (count >= min_periods) and (ddof <= count); + bool output_is_valid = count > 0 and (count >= min_periods); if (output_is_valid) { - // Welford algorithm - // See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance - OutputType m{0}, m2{0}; - size_type running_count{0}; - - for (size_type i = start_index; i < end_index; i++) { - if (has_nulls and input.is_null_nocheck(i)) { continue; } - - OutputType const x = static_cast(input.element(i)); - - running_count++; - OutputType const tmp1 = x - m; - m += tmp1 / running_count; - OutputType const tmp2 = x - m; - m2 += tmp1 * tmp2; - } - if constexpr (is_fixed_point()) { - // For fixed_point types, the previous computed value used unscaled rep-value, - // the final result should be multiplied by the square of decimal `scale`. - OutputType scaleby = exp10(static_cast(input.type().scale())); - scaleby *= scaleby; - output.element(current_index) = m2 / (count - ddof) * scaleby; + if (count >= ddof) { + // Welford algorithm + // See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance + OutputType m{0}, m2{0}; + size_type running_count{0}; + + for (size_type i = start_index; i < end_index; i++) { + if (has_nulls and input.is_null_nocheck(i)) { continue; } + + OutputType const x = static_cast(input.element(i)); + + running_count++; + OutputType const tmp1 = x - m; + m += tmp1 / running_count; + OutputType const tmp2 = x - m; + m2 += tmp1 * tmp2; + } + if constexpr (is_fixed_point()) { + // For fixed_point types, the previous computed value used unscaled rep-value, + // the final result should be multiplied by the square of decimal `scale`. + OutputType scaleby = exp10(static_cast(input.type().scale())); + scaleby *= scaleby; + output.element(current_index) = m2 / (count - ddof) * scaleby; + } else { + output.element(current_index) = m2 / (count - ddof); + } } else { - output.element(current_index) = m2 / (count - ddof); + output.element(current_index) = + cuda::std::numeric_limits::signaling_NaN(); } } diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index f020c214804..fee9bb5ce52 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -1254,13 +1254,14 @@ TYPED_TEST(FixedPointTests, VarStd) using fp_wrapper = cudf::test::fixed_point_column_wrapper; using fw_wrapper = cudf::test::fixed_width_column_wrapper; + double const nan = std::numeric_limits::signaling_NaN(); double const inf = std::numeric_limits::infinity(); size_type preceding_window{3}, following_window{0}, min_periods{1}, ddof{2}; // The variance of `input` given `scale` == 0 std::vector result_base_v{ - -1, inf, 1882804.66666666667, 1928018.666666666667, 1874.6666666666667, 2.0}; - std::vector result_mask_v{0, 1, 1, 1, 1, 1}; + nan, inf, 1882804.66666666667, 1928018.666666666667, 1874.6666666666667, 2.0}; + std::vector result_mask_v{1, 1, 1, 1, 1, 1}; // var tests for (int32_t s = -2; s <= 2; s++) { From d3cedb100fef169fb6bae543ea46a31874a55664 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 31 Aug 2021 16:29:36 -0700 Subject: [PATCH 37/47] make operator constant --- cpp/src/rolling/rolling_detail.cuh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index d76bd118942..b687bd438a7 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -289,8 +289,8 @@ struct DeviceRollingCountAll { */ template struct DeviceRollingVariance { - size_type min_periods; - size_type ddof; + size_type const min_periods; + size_type const ddof; // what operations do we support template @@ -310,7 +310,7 @@ struct DeviceRollingVariance { mutable_column_device_view& output, size_type start_index, size_type end_index, - size_type current_index) + size_type current_index) const { using DeviceInputType = device_storage_type_t; From 75e81400e070e7e3481db7832e8ea6af91c06438 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 31 Aug 2021 17:58:51 -0700 Subject: [PATCH 38/47] header cleanup --- cpp/src/rolling/rolling.cu | 32 +++++++++++++++--------------- cpp/src/rolling/rolling_detail.cuh | 10 ---------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/cpp/src/rolling/rolling.cu b/cpp/src/rolling/rolling.cu index eb81f81ef12..ab0f78bcf5d 100644 --- a/cpp/src/rolling/rolling.cu +++ b/cpp/src/rolling/rolling.cu @@ -18,21 +18,6 @@ #include "rolling_detail.cuh" namespace cudf { - -// Applies a fixed-size rolling window function to the values in a column. -std::unique_ptr rolling_window(column_view const& input, - size_type preceding_window, - size_type following_window, - size_type min_periods, - rolling_aggregation const& agg, - rmm::mr::device_memory_resource* mr) -{ - auto defaults = - cudf::is_dictionary(input.type()) ? dictionary_column_view(input).indices() : input; - return rolling_window( - input, empty_like(defaults)->view(), preceding_window, following_window, min_periods, agg, mr); -} - namespace detail { // Applies a fixed-size rolling window function to the values in a column. @@ -127,7 +112,8 @@ std::unique_ptr rolling_window(column_view const& input, } // namespace detail -// Applies a fixed-size rolling window function to the values in a column. +// Applies a fixed-size rolling window function to the values in a column, with default output +// specified std::unique_ptr rolling_window(column_view const& input, column_view const& default_outputs, size_type preceding_window, @@ -146,6 +132,20 @@ std::unique_ptr rolling_window(column_view const& input, mr); } +// Applies a fixed-size rolling window function to the values in a column, without default specified +std::unique_ptr rolling_window(column_view const& input, + size_type preceding_window, + size_type following_window, + size_type min_periods, + rolling_aggregation const& agg, + rmm::mr::device_memory_resource* mr) +{ + auto defaults = + cudf::is_dictionary(input.type()) ? dictionary_column_view(input).indices() : input; + return rolling_window( + input, empty_like(defaults)->view(), preceding_window, following_window, min_periods, agg, mr); +} + // Applies a variable-size rolling window function to the values in a column. std::unique_ptr rolling_window(column_view const& input, column_view const& preceding_window, diff --git a/cpp/src/rolling/rolling_detail.cuh b/cpp/src/rolling/rolling_detail.cuh index b687bd438a7..4cddfe46a66 100644 --- a/cpp/src/rolling/rolling_detail.cuh +++ b/cpp/src/rolling/rolling_detail.cuh @@ -26,22 +26,17 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include -#include #include #include #include -#include -#include #include #include #include @@ -57,13 +52,8 @@ #include #include -#include -#include -#include #include #include -#include -#include #include From 0317dedd1c1c219b73ad5e47a36a44d9af95b0d5 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Tue, 31 Aug 2021 22:07:01 -0700 Subject: [PATCH 39/47] Add groupby var/std tests --- python/cudf/cudf/tests/test_rolling.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/python/cudf/cudf/tests/test_rolling.py b/python/cudf/cudf/tests/test_rolling.py index 4445ac09389..b3c4c54d0a6 100644 --- a/python/cudf/cudf/tests/test_rolling.py +++ b/python/cudf/cudf/tests/test_rolling.py @@ -355,7 +355,9 @@ def some_func(A): ) -@pytest.mark.parametrize("agg", ["sum", "min", "max", "mean", "count"]) +@pytest.mark.parametrize( + "agg", ["sum", "min", "max", "mean", "count", "var", "std"] +) def test_rolling_groupby_simple(agg): pdf = pd.DataFrame( { @@ -385,7 +387,9 @@ def test_rolling_groupby_simple(agg): assert_eq(expect, got, check_dtype=False) -@pytest.mark.parametrize("agg", ["sum", "min", "max", "mean", "count"]) +@pytest.mark.parametrize( + "agg", ["sum", "min", "max", "mean", "count", "var", "std"] +) def test_rolling_groupby_multi(agg): pdf = pd.DataFrame( { @@ -406,7 +410,9 @@ def test_rolling_groupby_multi(agg): assert_eq(expect, got, check_dtype=False) -@pytest.mark.parametrize("agg", ["sum", "min", "max", "mean", "count"]) +@pytest.mark.parametrize( + "agg", ["sum", "min", "max", "mean", "count", "var", "std"] +) @pytest.mark.parametrize( "window_size", ["1d", "2d", "3d", "4d", "5d", "6d", "7d"] ) From d5359bad70c2b31bd5dd9b450e46c6f74b67a9c3 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 1 Sep 2021 09:17:54 -0700 Subject: [PATCH 40/47] Update cpp/tests/rolling/rolling_test.cpp Co-authored-by: David Wendt <45795991+davidwendt@users.noreply.github.com> --- cpp/tests/rolling/rolling_test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index fee9bb5ce52..61ae5d5d8d0 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -411,7 +411,6 @@ TYPED_TEST_CASE(RollingVarStdTest, cudf::test::FixedWidthTypesWithoutChrono); class RollingtVarStdTestUntyped : public cudf::test::BaseFixture { }; -// // ------------- expected failures -------------------- class RollingErrorTest : public cudf::test::BaseFixture { }; From a14d77e67401e571a3a68c19378364aa02a1a206 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 1 Sep 2021 09:40:27 -0700 Subject: [PATCH 41/47] style --- cpp/tests/rolling/rolling_test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/tests/rolling/rolling_test.cpp b/cpp/tests/rolling/rolling_test.cpp index 61ae5d5d8d0..a83e5886df5 100644 --- a/cpp/tests/rolling/rolling_test.cpp +++ b/cpp/tests/rolling/rolling_test.cpp @@ -411,7 +411,6 @@ TYPED_TEST_CASE(RollingVarStdTest, cudf::test::FixedWidthTypesWithoutChrono); class RollingtVarStdTestUntyped : public cudf::test::BaseFixture { }; - class RollingErrorTest : public cudf::test::BaseFixture { }; From fc6fe6a8a68b1fa7ac622fa057a2c2b2ae14bad1 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Sep 2021 14:02:12 -0700 Subject: [PATCH 42/47] Use custom dataframe generator --- python/cudf/cudf/testing/dataset_generator.py | 29 +------ python/cudf/cudf/tests/test_rolling.py | 87 ++++++++++++------- 2 files changed, 60 insertions(+), 56 deletions(-) diff --git a/python/cudf/cudf/testing/dataset_generator.py b/python/cudf/cudf/testing/dataset_generator.py index 825ccfa3470..cdea22a05af 100644 --- a/python/cudf/cudf/testing/dataset_generator.py +++ b/python/cudf/cudf/testing/dataset_generator.py @@ -368,22 +368,6 @@ def rand_dataframe( dtype=dtype, ) ) - elif dtype == "decimal32": - max_precision = meta.get( - "max_precision", cudf.Decimal32Dtype.MAX_PRECISION - ) - precision = np.random.randint(1, max_precision) - scale = np.random.randint(0, precision) - dtype = cudf.Decimal32Dtype(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( @@ -490,11 +474,8 @@ def int_generator(dtype, size): Generator for int data """ iinfo = np.iinfo(dtype) - # return lambda: np.random.randint( - # low=iinfo.min, high=iinfo.max, size=size, dtype=dtype, - # ) return lambda: np.random.randint( - low=-100, high=100, size=size, dtype=dtype, + low=iinfo.min, high=iinfo.max, size=size, dtype=dtype, ) @@ -504,11 +485,9 @@ def float_generator(dtype, size): """ finfo = np.finfo(dtype) return ( - # lambda: np.random.uniform( - # low=finfo.min / 2, high=finfo.max / 2, size=size, - # ) - # * 2 - lambda: np.random.uniform(low=-100, high=100, size=size,) + lambda: np.random.uniform( + low=finfo.min / 2, high=finfo.max / 2, size=size, + ) * 2 ) diff --git a/python/cudf/cudf/tests/test_rolling.py b/python/cudf/cudf/tests/test_rolling.py index 1c1a0023dca..9f66cffdf39 100644 --- a/python/cudf/cudf/tests/test_rolling.py +++ b/python/cudf/cudf/tests/test_rolling.py @@ -1,6 +1,7 @@ # Copyright (c) 2021, NVIDIA CORPORATION. import math +from decimal import Decimal import numpy as np import pandas as pd @@ -131,54 +132,78 @@ def test_rolling_with_offset(agg): ) +def generate_large_dataframe_for_var(size, window_size, nulls_prob, seed): + np.random.seed(seed) + + iupper_bound = math.sqrt(np.iinfo(np.int64).max / window_size) + ilower_bound = -math.sqrt(abs(np.iinfo(np.int64).min) / window_size) + + fupper_bound = math.sqrt(np.finfo(np.float64).max / window_size) + flower_bound = -math.sqrt(abs(np.finfo(np.float64).min) / window_size) + + intcol = [int(np.random.randint(ilower_bound, iupper_bound))] + floatcol = [ + np.random.uniform(flower_bound, fupper_bound) + if np.random.uniform(0, 1) > nulls_prob + else np.nan + for _ in range(size) + ] + deccol = [ + Decimal(np.random.randint(ilower_bound, iupper_bound)) + if np.random.uniform(0, 1) > nulls_prob + else None + for _ in range(size) + ] + + pdf = pd.DataFrame() + pdf["int"] = pd.Series(intcol).astype("int64") + pdf["float"] = pd.Series(floatcol).astype("float64") + pdf["decimal"] = pd.Series(deccol) + + return pdf + + @pytest.mark.parametrize("agg", ["std", "var"]) +@pytest.mark.parametrize("ddof", [0, 1]) @pytest.mark.parametrize("center", [True, False]) @pytest.mark.parametrize("seed", [100, 1000, 10000]) @pytest.mark.parametrize("window_size", [2, 10, 100, 1000]) -def test_rolling_var_std_large(agg, center, seed, window_size): +def test_rolling_var_std_large(agg, ddof, center, seed, window_size): + if PANDAS_GE_110: kwargs = {"check_freq": False} else: kwargs = {} n_rows = 1_000 - data = dataset_generator.rand_dataframe( - dtypes_meta=[ - {"dtype": "i4", "null_frequency": 0.4, "cardinality": 100}, - {"dtype": "f8", "null_frequency": 0.4, "cardinality": 100}, - {"dtype": "decimal64", "null_frequency": 0.4, "cardinality": 100}, - {"dtype": "decimal32", "null_frequency": 0.4, "cardinality": 100}, - ], - rows=n_rows, - use_threads=False, - seed=seed, + pdf = generate_large_dataframe_for_var( + n_rows, window_size, nulls_prob=0.4, seed=seed ) - - pdf = data.to_pandas() gdf = cudf.from_pandas(pdf) - expect = getattr(pdf.rolling(window_size, 1, center), agg)().fillna(-1) - got = getattr(gdf.rolling(window_size, 1, center), agg)().fillna(-1) - - # Pandas adopts an online variance calculation algorithm. Each window has a - # numeric error from the previous window. This makes the variance of a - # uniform window has a small residue. Taking the square root of a very - # small number may result in a non-trival number. - # - # In cudf, each window is computed independently from the previous window, - # this gives better numeric precision. - # - # For quantitative analysis: - # https://gist.github.com/isVoid/4984552da6ef5545348399c22d72cffb - # - # To make up this difference, we skip comparing uniform windows by coercing - # pandas result of these windows to 0. - for col in ["1", "2", "3"]: - expect[col][(got[col] == 0.0).to_pandas()] = 0.0 + expect = getattr(pdf.rolling(window_size, 1, center), agg)(ddof=ddof) + got = getattr(gdf.rolling(window_size, 1, center), agg)(ddof=ddof) assert_eq(expect, got, **kwargs) +@pytest.mark.xfail +def test_rolling_var_uniform_window(): + """ + Pandas adopts an online variance calculation algorithm. This gives a + floating point artifact. + + In cudf, each window is computed independently from the previous window, + this gives better numeric precision. + """ + + s = pd.Series([1e8, 5, 5, 5]) + expected = s.rolling(3).var() + got = cudf.from_pandas(s).rolling(3).var() + + assert_eq(expected, got) + + def test_rolling_count_with_offset(): """ This test covers the xfail case from test_rolling_with_offset["count"]. From aadc203ddd456aa2c5faa225776f4a650bd7d8a5 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Wed, 8 Sep 2021 14:24:37 -0700 Subject: [PATCH 43/47] styles --- python/cudf/cudf/_lib/rolling.pyx | 10 ++++++++-- python/cudf/cudf/tests/test_rolling.py | 1 - 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/python/cudf/cudf/_lib/rolling.pyx b/python/cudf/cudf/_lib/rolling.pyx index 7d5a48e7f5a..b4b3384032c 100644 --- a/python/cudf/cudf/_lib/rolling.pyx +++ b/python/cudf/cudf/_lib/rolling.pyx @@ -17,8 +17,14 @@ from cudf._lib.cpp.rolling cimport rolling_window as cpp_rolling_window from cudf._lib.cpp.types cimport size_type -def rolling(Column source_column, Column pre_column_window, - Column fwd_column_window, window, min_periods, center, op, agg_params): +def rolling(Column source_column, + Column pre_column_window, + Column fwd_column_window, + window, + min_periods, + center, + op, + agg_params): """ Rolling on input executing operation within the given window for each row diff --git a/python/cudf/cudf/tests/test_rolling.py b/python/cudf/cudf/tests/test_rolling.py index 9f66cffdf39..929114e8e11 100644 --- a/python/cudf/cudf/tests/test_rolling.py +++ b/python/cudf/cudf/tests/test_rolling.py @@ -8,7 +8,6 @@ import pytest import cudf -import cudf.testing.dataset_generator as dataset_generator from cudf.core._compat import PANDAS_GE_110 from cudf.testing._utils import assert_eq From b5a7f735b9e2bf9580bd91eb46e1d94e28e7a5bc Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 9 Sep 2021 16:41:31 -0700 Subject: [PATCH 44/47] Fix seeds for rngs. --- python/cudf/cudf/tests/test_rolling.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/python/cudf/cudf/tests/test_rolling.py b/python/cudf/cudf/tests/test_rolling.py index 929114e8e11..52403ed6505 100644 --- a/python/cudf/cudf/tests/test_rolling.py +++ b/python/cudf/cudf/tests/test_rolling.py @@ -27,16 +27,17 @@ @pytest.mark.parametrize("nulls", ["none", "one", "some", "all"]) @pytest.mark.parametrize("center", [True, False]) def test_rolling_series_basic(data, index, agg, nulls, center): + rng = np.random.default_rng(1) if PANDAS_GE_110: kwargs = {"check_freq": False} else: kwargs = {} if len(data) > 0: if nulls == "one": - p = np.random.randint(0, len(data)) + p = rng.integers(0, len(data)) data[p] = np.nan elif nulls == "some": - p1, p2 = np.random.randint(0, len(data), (2,)) + p1, p2 = rng.integers(0, len(data), (2,)) data[p1] = np.nan data[p2] = np.nan elif nulls == "all": @@ -73,15 +74,16 @@ def test_rolling_series_basic(data, index, agg, nulls, center): @pytest.mark.parametrize("nulls", ["none", "one", "some", "all"]) @pytest.mark.parametrize("center", [True, False]) def test_rolling_dataframe_basic(data, agg, nulls, center): + rng = np.random.default_rng(0) pdf = pd.DataFrame(data) if len(pdf) > 0: for col_name in pdf.columns: if nulls == "one": - p = np.random.randint(0, len(data)) + p = rng.integers(0, len(data)) pdf[col_name][p] = np.nan elif nulls == "some": - p1, p2 = np.random.randint(0, len(data), (2,)) + p1, p2 = rng.integers(0, len(data), (2,)) pdf[col_name][p1] = np.nan pdf[col_name][p2] = np.nan elif nulls == "all": @@ -132,7 +134,7 @@ def test_rolling_with_offset(agg): def generate_large_dataframe_for_var(size, window_size, nulls_prob, seed): - np.random.seed(seed) + rng = np.random.default_rng(seed) iupper_bound = math.sqrt(np.iinfo(np.int64).max / window_size) ilower_bound = -math.sqrt(abs(np.iinfo(np.int64).min) / window_size) @@ -140,16 +142,17 @@ def generate_large_dataframe_for_var(size, window_size, nulls_prob, seed): fupper_bound = math.sqrt(np.finfo(np.float64).max / window_size) flower_bound = -math.sqrt(abs(np.finfo(np.float64).min) / window_size) - intcol = [int(np.random.randint(ilower_bound, iupper_bound))] + # Nullable integer type rolling agg is unsupported in pandas + intcol = rng.integers(ilower_bound, iupper_bound, size) floatcol = [ - np.random.uniform(flower_bound, fupper_bound) - if np.random.uniform(0, 1) > nulls_prob + rng.uniform(flower_bound, fupper_bound) + if rng.uniform(0, 1) > nulls_prob else np.nan for _ in range(size) ] deccol = [ - Decimal(np.random.randint(ilower_bound, iupper_bound)) - if np.random.uniform(0, 1) > nulls_prob + Decimal(int(rng.integers(ilower_bound, iupper_bound))) + if rng.uniform(0, 1) > nulls_prob else None for _ in range(size) ] @@ -168,7 +171,6 @@ def generate_large_dataframe_for_var(size, window_size, nulls_prob, seed): @pytest.mark.parametrize("seed", [100, 1000, 10000]) @pytest.mark.parametrize("window_size", [2, 10, 100, 1000]) def test_rolling_var_std_large(agg, ddof, center, seed, window_size): - if PANDAS_GE_110: kwargs = {"check_freq": False} else: @@ -191,6 +193,7 @@ def test_rolling_var_uniform_window(): """ Pandas adopts an online variance calculation algorithm. This gives a floating point artifact. + https://github.com/pandas-dev/pandas/issues/37051 In cudf, each window is computed independently from the previous window, this gives better numeric precision. From fce541ea46ea7e813afdc1d027eaeb8d982b07a1 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 9 Sep 2021 16:47:57 -0700 Subject: [PATCH 45/47] Doc for dataframe gen --- python/cudf/cudf/tests/test_rolling.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/cudf/cudf/tests/test_rolling.py b/python/cudf/cudf/tests/test_rolling.py index 52403ed6505..c2decf5c014 100644 --- a/python/cudf/cudf/tests/test_rolling.py +++ b/python/cudf/cudf/tests/test_rolling.py @@ -134,6 +134,11 @@ def test_rolling_with_offset(agg): def generate_large_dataframe_for_var(size, window_size, nulls_prob, seed): + """Generates a random number filled dataframe with nulls to evaluate + correctness of variance and std. The range of the numbers are clamped + to avoid overflows. Three dtypes were tested: `np.int64` (non-nullable + integer), `np.float64` and `Decimal`. + """ rng = np.random.default_rng(seed) iupper_bound = math.sqrt(np.iinfo(np.int64).max / window_size) From 2ca83c69fc032a6ce26b60df015759e35d4e0cf4 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 9 Sep 2021 22:28:49 -0700 Subject: [PATCH 46/47] Switch to `dataframe_generator` --- python/cudf/cudf/tests/test_rolling.py | 78 ++++++++++++-------------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/python/cudf/cudf/tests/test_rolling.py b/python/cudf/cudf/tests/test_rolling.py index c2decf5c014..8551448133d 100644 --- a/python/cudf/cudf/tests/test_rolling.py +++ b/python/cudf/cudf/tests/test_rolling.py @@ -10,6 +10,7 @@ import cudf from cudf.core._compat import PANDAS_GE_110 from cudf.testing._utils import assert_eq +from cudf.testing.dataset_generator import rand_dataframe @pytest.mark.parametrize( @@ -133,58 +134,53 @@ def test_rolling_with_offset(agg): ) -def generate_large_dataframe_for_var(size, window_size, nulls_prob, seed): - """Generates a random number filled dataframe with nulls to evaluate - correctness of variance and std. The range of the numbers are clamped - to avoid overflows. Three dtypes were tested: `np.int64` (non-nullable - integer), `np.float64` and `Decimal`. - """ - rng = np.random.default_rng(seed) - - iupper_bound = math.sqrt(np.iinfo(np.int64).max / window_size) - ilower_bound = -math.sqrt(abs(np.iinfo(np.int64).min) / window_size) - - fupper_bound = math.sqrt(np.finfo(np.float64).max / window_size) - flower_bound = -math.sqrt(abs(np.finfo(np.float64).min) / window_size) - - # Nullable integer type rolling agg is unsupported in pandas - intcol = rng.integers(ilower_bound, iupper_bound, size) - floatcol = [ - rng.uniform(flower_bound, fupper_bound) - if rng.uniform(0, 1) > nulls_prob - else np.nan - for _ in range(size) - ] - deccol = [ - Decimal(int(rng.integers(ilower_bound, iupper_bound))) - if rng.uniform(0, 1) > nulls_prob - else None - for _ in range(size) - ] - - pdf = pd.DataFrame() - pdf["int"] = pd.Series(intcol).astype("int64") - pdf["float"] = pd.Series(floatcol).astype("float64") - pdf["decimal"] = pd.Series(deccol) - - return pdf - - @pytest.mark.parametrize("agg", ["std", "var"]) @pytest.mark.parametrize("ddof", [0, 1]) @pytest.mark.parametrize("center", [True, False]) -@pytest.mark.parametrize("seed", [100, 1000, 10000]) -@pytest.mark.parametrize("window_size", [2, 10, 100, 1000]) +@pytest.mark.parametrize("seed", [100, 2000]) +@pytest.mark.parametrize("window_size", [2, 10, 100]) def test_rolling_var_std_large(agg, ddof, center, seed, window_size): if PANDAS_GE_110: kwargs = {"check_freq": False} else: kwargs = {} + iupper_bound = math.sqrt(np.iinfo(np.int64).max / window_size) + ilower_bound = -math.sqrt(abs(np.iinfo(np.int64).min) / window_size) + + fupper_bound = math.sqrt(np.finfo(np.float64).max / window_size) + flower_bound = -math.sqrt(abs(np.finfo(np.float64).min) / window_size) + n_rows = 1_000 - pdf = generate_large_dataframe_for_var( - n_rows, window_size, nulls_prob=0.4, seed=seed + data = rand_dataframe( + dtypes_meta=[ + { + "dtype": "int64", + "null_frequency": 0.4, + "cardinality": n_rows, + "min_bound": ilower_bound, + "max_bound": iupper_bound, + }, + { + "dtype": "float64", + "null_frequency": 0.4, + "cardinality": n_rows, + "min_bound": flower_bound, + "max_bound": fupper_bound, + }, + { + "dtype": "decimal64", + "null_frequency": 0.4, + "cardinality": n_rows, + "min_bound": ilower_bound, + "max_bound": iupper_bound, + }, + ], + rows=n_rows, + use_threads=False, + seed=seed, ) + pdf = data.to_pandas() gdf = cudf.from_pandas(pdf) expect = getattr(pdf.rolling(window_size, 1, center), agg)(ddof=ddof) From 0f6c359f5992227d32dff1cf5e006ada25355509 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 9 Sep 2021 22:34:41 -0700 Subject: [PATCH 47/47] style --- python/cudf/cudf/tests/test_rolling.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/cudf/cudf/tests/test_rolling.py b/python/cudf/cudf/tests/test_rolling.py index 8551448133d..29272cbf876 100644 --- a/python/cudf/cudf/tests/test_rolling.py +++ b/python/cudf/cudf/tests/test_rolling.py @@ -1,7 +1,6 @@ # Copyright (c) 2021, NVIDIA CORPORATION. import math -from decimal import Decimal import numpy as np import pandas as pd