From d1bc220c87ee8ee7d24f8a7b50fb17e3f31f22ac Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 14 May 2026 18:02:37 +0000 Subject: [PATCH 01/34] refactor row-IR --- .../cudf/ast/detail/operator_functor.cuh | 4 +- cpp/include/cudf/ast/expressions.hpp | 8 +- cpp/src/ast/expressions.cpp | 19 +- cpp/src/jit/column_accessor.cuh | 11 +- cpp/src/jit/join_column_accessor.cuh | 129 --- cpp/src/jit/row_ir.cpp | 946 ++++++++---------- cpp/src/jit/row_ir.hpp | 638 +++++------- cpp/src/join/filter_join_indices_jit.cu | 206 ++-- cpp/src/join/jit/filter_join_kernel.cu | 75 +- cpp/src/join/jit/filter_join_kernel.cuh | 21 +- cpp/src/stream_compaction/filter/filter.cu | 123 +-- cpp/src/transform/transform.cu | 29 +- cpp/tests/jit/row_ir.cpp | 320 +++--- 13 files changed, 1119 insertions(+), 1410 deletions(-) delete mode 100644 cpp/src/jit/join_column_accessor.cuh diff --git a/cpp/include/cudf/ast/detail/operator_functor.cuh b/cpp/include/cudf/ast/detail/operator_functor.cuh index 043b2c47746d..e245f0288be6 100644 --- a/cpp/include/cudf/ast/detail/operator_functor.cuh +++ b/cpp/include/cudf/ast/detail/operator_functor.cuh @@ -778,9 +778,9 @@ struct operator_functor { } }; -constexpr bool flatten_predicate(possibly_null_value_t value) { return value; } +constexpr bool predicate(possibly_null_value_t value) { return value; } -constexpr bool flatten_predicate(possibly_null_value_t value) +constexpr bool predicate(possibly_null_value_t value) { return value.has_value() && *value; } diff --git a/cpp/include/cudf/ast/expressions.hpp b/cpp/include/cudf/ast/expressions.hpp index 76fdf2d10120..22ace86028e0 100644 --- a/cpp/include/cudf/ast/expressions.hpp +++ b/cpp/include/cudf/ast/expressions.hpp @@ -59,7 +59,7 @@ class expression_transformer; * This class is a part of a "visitor" pattern with the `expression_parser` class. * Expressions inheriting from this class can accept parsers as visitors. */ -struct expression { +struct [[nodiscard]] expression { /** * @brief Accepts a visitor class. * @@ -514,17 +514,17 @@ class operation : public expression { namespace detail { -/// @brief An expression that represents a filter predicate. +/// @brief An expression that represents a predicate. /// /// This is an internal expression used in filter operations. It is not intended to be used by /// external code and is not a part of the public API. -class filter_predicate : public expression { +class predicate : public expression { public: /** * @brief Construct a new filter predicate object * @param source The source expression from which the predicate value is taken */ - filter_predicate(expression const& source) : source_{source} {} + predicate(expression const& source) : source_{source} {} /** * @copydoc expression::accept diff --git a/cpp/src/ast/expressions.cpp b/cpp/src/ast/expressions.cpp index d51d3f323498..9e6b8cf3ce8c 100644 --- a/cpp/src/ast/expressions.cpp +++ b/cpp/src/ast/expressions.cpp @@ -80,25 +80,24 @@ bool operation::may_evaluate_null(table_view const& left, }); }; -cudf::size_type detail::filter_predicate::accept(detail::expression_parser& visitor) const +cudf::size_type detail::predicate::accept(detail::expression_parser& visitor) const { - CUDF_FAIL( - "filter_predicate is an internal expression and should not be visited by expression_parser", - std::invalid_argument); + CUDF_FAIL("predicate is an internal expression and should not be visited by expression_parser", + std::invalid_argument); } -std::reference_wrapper detail::filter_predicate::accept( +std::reference_wrapper detail::predicate::accept( detail::expression_transformer& visitor) const { CUDF_FAIL( - "filter_predicate is an internal expression and should not be visited by " + "predicate is an internal expression and should not be visited by " "expression_transformer", std::invalid_argument); } -bool detail::filter_predicate::may_evaluate_null(table_view const& left, - table_view const& right, - rmm::cuda_stream_view stream) const +bool detail::predicate::may_evaluate_null(table_view const& left, + table_view const& right, + rmm::cuda_stream_view stream) const { return false; } @@ -135,7 +134,7 @@ std::unique_ptr column_name_reference::accept( std::invalid_argument); } -std::unique_ptr detail::filter_predicate::accept( +std::unique_ptr detail::predicate::accept( cudf::detail::row_ir::ast_converter& converter) const { return converter.add_ir_node(*this); diff --git a/cpp/src/jit/column_accessor.cuh b/cpp/src/jit/column_accessor.cuh index 303e8cb029f3..ef1536cb409d 100644 --- a/cpp/src/jit/column_accessor.cuh +++ b/cpp/src/jit/column_accessor.cuh @@ -16,12 +16,13 @@ namespace cudf { namespace jit { -template +template struct column_accessor { - static constexpr int32_t index = Index; - using column_type = Column; - using element_type = Element; - using optional_element_type = cuda::std::optional; + static constexpr int32_t index = Index; + static constexpr int32_t table_index = TableIndex; + using column_type = Column; + using element_type = Element; + using optional_element_type = cuda::std::optional; static constexpr bool as_scalar = AsScalar; diff --git a/cpp/src/jit/join_column_accessor.cuh b/cpp/src/jit/join_column_accessor.cuh deleted file mode 100644 index e78a9ec1991c..000000000000 --- a/cpp/src/jit/join_column_accessor.cuh +++ /dev/null @@ -1,129 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once -#include -#include - -#include -#include - -namespace cudf { -namespace jit { - -// Join-specific accessor for indexed table access. -// Receives both left and right table pointers plus both row indices, -// and selects the appropriate table based on the Side template parameter. -enum class join_side : bool { LEFT, RIGHT }; - -template -struct join_column_accessor { - using type = T; - static constexpr int32_t index = Index; - - static __device__ T element(cudf::column_device_view_core const* left_tables, - cudf::column_device_view_core const* right_tables, - cudf::size_type left_row_idx, - cudf::size_type right_row_idx, - cudf::size_type /* thread_idx */) - { - if constexpr (Side == join_side::LEFT) { - return left_tables[index].template element(left_row_idx); - } else { - return right_tables[index].template element(right_row_idx); - } - } - - static __device__ bool is_null(cudf::column_device_view_core const* left_tables, - cudf::column_device_view_core const* right_tables, - cudf::size_type left_row_idx, - cudf::size_type right_row_idx, - cudf::size_type /* thread_idx */) - { - if constexpr (Side == join_side::LEFT) { - return left_tables[index].is_null(left_row_idx); - } else { - return right_tables[index].is_null(right_row_idx); - } - } - - static __device__ bool is_valid(cudf::column_device_view_core const* left_tables, - cudf::column_device_view_core const* right_tables, - cudf::size_type left_row_idx, - cudf::size_type right_row_idx, - cudf::size_type /* thread_idx */) - { - if constexpr (Side == join_side::LEFT) { - return left_tables[index].is_valid(left_row_idx); - } else { - return right_tables[index].is_valid(right_row_idx); - } - } - - static __device__ cuda::std::optional nullable_element( - cudf::column_device_view_core const* left_tables, - cudf::column_device_view_core const* right_tables, - cudf::size_type left_row_idx, - cudf::size_type right_row_idx, - cudf::size_type thread_idx) - { - if (is_null(left_tables, right_tables, left_row_idx, right_row_idx, thread_idx)) { - return cuda::std::nullopt; - } - return element(left_tables, right_tables, left_row_idx, right_row_idx, thread_idx); - } -}; - -// Join-specific accessor for scalar (literal) values. -// Scalar columns are appended to the left table's device views. -// Always reads at row 0 since scalar columns have size 1. -template -struct join_scalar_accessor { - using type = T; - static constexpr int32_t index = Index; - - static __device__ T element(cudf::column_device_view_core const* left_tables, - cudf::column_device_view_core const*, - cudf::size_type, - cudf::size_type, - cudf::size_type) - { - return left_tables[index].template element(0); - } - - static __device__ bool is_null(cudf::column_device_view_core const* left_tables, - cudf::column_device_view_core const*, - cudf::size_type, - cudf::size_type, - cudf::size_type) - { - return left_tables[index].is_null(0); - } - - static __device__ bool is_valid(cudf::column_device_view_core const* left_tables, - cudf::column_device_view_core const*, - cudf::size_type, - cudf::size_type, - cudf::size_type) - { - return left_tables[index].is_valid(0); - } - - static __device__ cuda::std::optional nullable_element( - cudf::column_device_view_core const* left_tables, - cudf::column_device_view_core const* right_tables, - cudf::size_type left_row_idx, - cudf::size_type right_row_idx, - cudf::size_type thread_idx) - { - if (is_null(left_tables, right_tables, left_row_idx, right_row_idx, thread_idx)) { - return cuda::std::nullopt; - } - return element(left_tables, right_tables, left_row_idx, right_row_idx, thread_idx); - } -}; - -} // namespace jit -} // namespace cudf diff --git a/cpp/src/jit/row_ir.cpp b/cpp/src/jit/row_ir.cpp index 82cd308c2f5d..acf17772c00c 100644 --- a/cpp/src/jit/row_ir.cpp +++ b/cpp/src/jit/row_ir.cpp @@ -12,668 +12,600 @@ #include #include #include -#include #include #include #include #include -namespace cudf { +namespace cudf::detail::row_ir { -namespace detail { - -namespace row_ir { - -std::string cuda_type(cudf::data_type type, bool nullable) +inline ast::ast_operator as_ast_op(opcode op) { - auto name = type_to_name(type); - return nullable ? std::format("cuda::std::optional<{}>", name) : name; + switch (op) { + case opcode::ADD: return ast::ast_operator::ADD; + case opcode::SUB: return ast::ast_operator::SUB; + case opcode::MUL: return ast::ast_operator::MUL; + case opcode::DIV: return ast::ast_operator::DIV; + case opcode::TRUE_DIV: return ast::ast_operator::TRUE_DIV; + case opcode::FLOOR_DIV: return ast::ast_operator::FLOOR_DIV; + case opcode::MOD: return ast::ast_operator::MOD; + case opcode::PYMOD: return ast::ast_operator::PYMOD; + case opcode::POW: return ast::ast_operator::POW; + case opcode::EQUAL: return ast::ast_operator::EQUAL; + case opcode::NULL_EQUAL: return ast::ast_operator::NULL_EQUAL; + case opcode::NOT_EQUAL: return ast::ast_operator::NOT_EQUAL; + case opcode::LESS: return ast::ast_operator::LESS; + case opcode::GREATER: return ast::ast_operator::GREATER; + case opcode::LESS_EQUAL: return ast::ast_operator::LESS_EQUAL; + case opcode::GREATER_EQUAL: return ast::ast_operator::GREATER_EQUAL; + case opcode::BITWISE_AND: return ast::ast_operator::BITWISE_AND; + case opcode::BITWISE_OR: return ast::ast_operator::BITWISE_OR; + case opcode::BITWISE_XOR: return ast::ast_operator::BITWISE_XOR; + case opcode::LOGICAL_AND: return ast::ast_operator::LOGICAL_AND; + case opcode::NULL_LOGICAL_AND: return ast::ast_operator::NULL_LOGICAL_AND; + case opcode::LOGICAL_OR: return ast::ast_operator::LOGICAL_OR; + case opcode::NULL_LOGICAL_OR: return ast::ast_operator::NULL_LOGICAL_OR; + case opcode::IDENTITY: return ast::ast_operator::IDENTITY; + case opcode::IS_NULL: return ast::ast_operator::IS_NULL; + case opcode::SIN: return ast::ast_operator::SIN; + case opcode::COS: return ast::ast_operator::COS; + case opcode::TAN: return ast::ast_operator::TAN; + case opcode::ARCSIN: return ast::ast_operator::ARCSIN; + case opcode::ARCCOS: return ast::ast_operator::ARCCOS; + case opcode::ARCTAN: return ast::ast_operator::ARCTAN; + case opcode::SINH: return ast::ast_operator::SINH; + case opcode::COSH: return ast::ast_operator::COSH; + case opcode::TANH: return ast::ast_operator::TANH; + case opcode::ARCSINH: return ast::ast_operator::ARCSINH; + case opcode::ARCCOSH: return ast::ast_operator::ARCCOSH; + case opcode::ARCTANH: return ast::ast_operator::ARCTANH; + case opcode::EXP: return ast::ast_operator::EXP; + case opcode::LOG: return ast::ast_operator::LOG; + case opcode::SQRT: return ast::ast_operator::SQRT; + case opcode::CBRT: return ast::ast_operator::CBRT; + case opcode::CEIL: return ast::ast_operator::CEIL; + case opcode::FLOOR: return ast::ast_operator::FLOOR; + case opcode::ABS: return ast::ast_operator::ABS; + case opcode::RINT: return ast::ast_operator::RINT; + case opcode::BIT_INVERT: return ast::ast_operator::BIT_INVERT; + case opcode::NOT: return ast::ast_operator::NOT; + case opcode::CAST_TO_INT64: return ast::ast_operator::CAST_TO_INT64; + case opcode::CAST_TO_UINT64: return ast::ast_operator::CAST_TO_UINT64; + case opcode::CAST_TO_FLOAT64: return ast::ast_operator::CAST_TO_FLOAT64; + default: CUDF_FAIL("Invalid operator type."); + } } -std::string instance_context::make_tmp_id() +inline opcode as_opcode(ast::ast_operator op) { - return std::format("{}{}", tmp_prefix_, num_tmp_vars_++); + switch (op) { + case ast::ast_operator::ADD: return opcode::ADD; + case ast::ast_operator::SUB: return opcode::SUB; + case ast::ast_operator::MUL: return opcode::MUL; + case ast::ast_operator::DIV: return opcode::DIV; + case ast::ast_operator::TRUE_DIV: return opcode::TRUE_DIV; + case ast::ast_operator::FLOOR_DIV: return opcode::FLOOR_DIV; + case ast::ast_operator::MOD: return opcode::MOD; + case ast::ast_operator::PYMOD: return opcode::PYMOD; + case ast::ast_operator::POW: return opcode::POW; + case ast::ast_operator::EQUAL: return opcode::EQUAL; + case ast::ast_operator::NULL_EQUAL: return opcode::NULL_EQUAL; + case ast::ast_operator::NOT_EQUAL: return opcode::NOT_EQUAL; + case ast::ast_operator::LESS: return opcode::LESS; + case ast::ast_operator::GREATER: return opcode::GREATER; + case ast::ast_operator::LESS_EQUAL: return opcode::LESS_EQUAL; + case ast::ast_operator::GREATER_EQUAL: return opcode::GREATER_EQUAL; + case ast::ast_operator::BITWISE_AND: return opcode::BITWISE_AND; + case ast::ast_operator::BITWISE_OR: return opcode::BITWISE_OR; + case ast::ast_operator::BITWISE_XOR: return opcode::BITWISE_XOR; + case ast::ast_operator::LOGICAL_AND: return opcode::LOGICAL_AND; + case ast::ast_operator::NULL_LOGICAL_AND: return opcode::NULL_LOGICAL_AND; + case ast::ast_operator::LOGICAL_OR: return opcode::LOGICAL_OR; + case ast::ast_operator::NULL_LOGICAL_OR: return opcode::NULL_LOGICAL_OR; + case ast::ast_operator::IDENTITY: return opcode::IDENTITY; + case ast::ast_operator::IS_NULL: return opcode::IS_NULL; + case ast::ast_operator::SIN: return opcode::SIN; + case ast::ast_operator::COS: return opcode::COS; + case ast::ast_operator::TAN: return opcode::TAN; + case ast::ast_operator::ARCSIN: return opcode::ARCSIN; + case ast::ast_operator::ARCCOS: return opcode::ARCCOS; + case ast::ast_operator::ARCTAN: return opcode::ARCTAN; + case ast::ast_operator::SINH: return opcode::SINH; + case ast::ast_operator::COSH: return opcode::COSH; + case ast::ast_operator::TANH: return opcode::TANH; + case ast::ast_operator::ARCSINH: return opcode::ARCSINH; + case ast::ast_operator::ARCCOSH: return opcode::ARCCOSH; + case ast::ast_operator::ARCTANH: return opcode::ARCTANH; + case ast::ast_operator::EXP: return opcode::EXP; + case ast::ast_operator::LOG: return opcode::LOG; + case ast::ast_operator::SQRT: return opcode::SQRT; + case ast::ast_operator::CBRT: return opcode::CBRT; + case ast::ast_operator::CEIL: return opcode::CEIL; + case ast::ast_operator::FLOOR: return opcode::FLOOR; + case ast::ast_operator::ABS: return opcode::ABS; + case ast::ast_operator::RINT: return opcode::RINT; + case ast::ast_operator::BIT_INVERT: return opcode::BIT_INVERT; + case ast::ast_operator::NOT: return opcode::NOT; + case ast::ast_operator::CAST_TO_INT64: return opcode::CAST_TO_INT64; + case ast::ast_operator::CAST_TO_UINT64: return opcode::CAST_TO_UINT64; + case ast::ast_operator::CAST_TO_FLOAT64: return opcode::CAST_TO_FLOAT64; + default: CUDF_FAIL("Invalid operator type."); + } } -bool instance_context::has_nulls() const { return has_nulls_; } - -void instance_context::set_has_nulls(bool has_nulls) { has_nulls_ = has_nulls; } - -get_input::get_input(int32_t input) : id_(), input_(input), type_() {} - -std::string_view get_input::get_id() { return id_; } - -data_type get_input::get_type() { return type_; } - -bool get_input::is_null_aware() { return false; } - -bool get_input::is_always_valid() { return false; } - -void get_input::instantiate(instance_context& ctx, instance_info const& info) +int32_t instance_context::add_output() { - id_ = ctx.make_tmp_id(); - auto const& input = info.inputs[input_]; - type_ = input.type; + auto id = static_cast(output_vars_.size()); + auto id_str = std::format("out_{}", id); + output_vars_.emplace_back(std::move(id_str)); + return id; } -std::string get_input::generate_code(instance_context& ctx, - target_info const& info, - instance_info const& instance) +int32_t instance_context::add_input(input in) { - switch (info.id) { - case target::CUDA: { - return std::format( - "{} {} = {};", cuda_type(type_, ctx.has_nulls()), id_, instance.inputs[input_].id); - } - default: - CUDF_FAIL("Unsupported target: " + std::to_string(static_cast(info.id)), - std::invalid_argument); + auto id = static_cast(inputs_.size()); + auto id_str = std::format("in_{}", id); + + data_type type{type_id::EMPTY, 0}; + if (auto* col = std::get_if(&in)) { + type = col->column.type(); + } else { + auto& scalar = std::get(in); + type = scalar.scalar_column->type(); } + inputs_.emplace_back(std::move(in)); + input_vars_.emplace_back(std::move(id_str), type); + return id; } -set_output::set_output(int32_t output, std::unique_ptr source) - : id_(), output_(output), source_(std::move(source)), type_(), output_id_() +std::string instance_context::make_tmp_id() { + return std::format("{}{}", tmp_prefix_, num_tmp_vars_++); } -std::string_view set_output::get_id() { return id_; } +bool instance_context::has_nulls() const { return has_nulls_; } -data_type set_output::get_type() { return type_; } +void instance_context::set_has_nulls(bool has_nulls) { has_nulls_ = has_nulls; } -bool set_output::is_null_aware() { return source_->is_null_aware(); } +std::span instance_context::get_inputs() const { return inputs_; } -bool set_output::is_always_valid() { return source_->is_always_valid(); } +std::span instance_context::get_input_vars() const { return input_vars_; } -node& set_output::get_source() { return *source_; } +std::span instance_context::get_output_vars() const { return output_vars_; } -void set_output::instantiate(instance_context& ctx, instance_info const& info) +node::node(opcode op, std::optional target_scale, std::vector> args) + : op_{op}, target_scale_{target_scale}, args_{std::move(args)} { - source_->instantiate(ctx, info); - id_ = ctx.make_tmp_id(); - auto source_type = source_->get_type(); - type_ = source_type; - output_id_ = info.outputs[output_].id; -} + CUDF_EXPECTS(op_ != opcode::GET_INPUT && op_ != opcode::SET_OUTPUT, + std::format("Invalid opcode `{}` for operation node.", static_cast(op_)), + std::runtime_error); + CUDF_EXPECTS( + op_ != opcode::RESCALE, "Opcode `RESCALE` is not implemented yet", std::runtime_error); -std::string set_output::generate_code(instance_context& ctx, - target_info const& info, - instance_info const& instance) -{ - switch (info.id) { - case target::CUDA: { - auto source_code = source_->generate_code(ctx, info, instance); - return std::format( - "{}\n" - "{} {} = {};\n" - "*{} = {};", - source_code, - cuda_type(type_, ctx.has_nulls()), - id_, - source_->get_id(), - output_id_, - id_); - } - default: - CUDF_FAIL("Unsupported target: " + std::to_string(static_cast(info.id)), - std::invalid_argument); - } + auto expected_arity = op == opcode::PREDICATE + ? 1 + : static_cast(ast::detail::ast_operator_arity(as_ast_op(op_))); + auto actual_arity = args_.size(); + CUDF_EXPECTS(actual_arity == expected_arity, + std::format("Invalid number of arguments for operator `{}`. Expected {}, Got {}.", + static_cast(op_), + expected_arity, + actual_arity), + std::runtime_error); } -operation::operation(opcode op, std::unique_ptr* move_begin, std::unique_ptr* move_end) - : id_(), op_(op), operands_(), type_() +node::node(input_reference input) : reference_{input}, op_{opcode::GET_INPUT} {} + +node::node(output_reference reference, std::unique_ptr arg) + : reference_{reference}, op_{opcode::SET_OUTPUT} { - operands_.insert( - operands_.begin(), std::make_move_iterator(move_begin), std::make_move_iterator(move_end)); - CUDF_EXPECTS(static_cast(operands_.size()) == ast::detail::ast_operator_arity(op), - "Invalid number of arguments for operator.", - std::invalid_argument); - CUDF_EXPECTS( - operands_.size() > 0, "Operator must have at least one operand", std::invalid_argument); + args_.emplace_back(std::move(arg)); } -operation::operation(opcode op, std::vector> operands) - : operation(op, operands.data(), operands.data() + operands.size()) +node::node(output_reference reference, node arg) + : node{reference, std::make_unique(std::move(arg))} { } -std::string_view operation::get_id() { return id_; } +std::string_view node::get_id() const { return id_; } + +data_type node::get_type() const { return type_; } + +std::optional node::get_target_scale() const { return target_scale_; } -data_type operation::get_type() { return type_; } +opcode node::get_opcode() const { return op_; } -inline bool is_operator_null_aware(opcode op) +std::span const> node::get_args() const { return args_; } + +inline bool get_op_requires_nulls(opcode op) { switch (op) { - case ast::ast_operator::IS_NULL: - case ast::ast_operator::NULL_EQUAL: - case ast::ast_operator::NULL_LOGICAL_AND: - case ast::ast_operator::NULL_LOGICAL_OR: return true; - - case ast::ast_operator::ADD: - case ast::ast_operator::SUB: - case ast::ast_operator::MUL: - case ast::ast_operator::DIV: - case ast::ast_operator::TRUE_DIV: - case ast::ast_operator::FLOOR_DIV: - case ast::ast_operator::MOD: - case ast::ast_operator::PYMOD: - case ast::ast_operator::POW: - case ast::ast_operator::NOT_EQUAL: - case ast::ast_operator::EQUAL: - case ast::ast_operator::LESS: - case ast::ast_operator::GREATER: - case ast::ast_operator::LESS_EQUAL: - case ast::ast_operator::GREATER_EQUAL: - case ast::ast_operator::BITWISE_AND: - case ast::ast_operator::BITWISE_OR: - case ast::ast_operator::BITWISE_XOR: - case ast::ast_operator::LOGICAL_AND: - case ast::ast_operator::LOGICAL_OR: - case ast::ast_operator::IDENTITY: - case ast::ast_operator::SIN: - case ast::ast_operator::COS: - case ast::ast_operator::TAN: - case ast::ast_operator::ARCSIN: - case ast::ast_operator::ARCCOS: - case ast::ast_operator::ARCTAN: - case ast::ast_operator::SINH: - case ast::ast_operator::COSH: - case ast::ast_operator::TANH: - case ast::ast_operator::ARCSINH: - case ast::ast_operator::ARCCOSH: - case ast::ast_operator::ARCTANH: - case ast::ast_operator::EXP: - case ast::ast_operator::LOG: - case ast::ast_operator::SQRT: - case ast::ast_operator::CBRT: - case ast::ast_operator::CEIL: - case ast::ast_operator::FLOOR: - case ast::ast_operator::ABS: - case ast::ast_operator::RINT: - case ast::ast_operator::BIT_INVERT: - case ast::ast_operator::NOT: - case ast::ast_operator::CAST_TO_INT64: - case ast::ast_operator::CAST_TO_UINT64: - case ast::ast_operator::CAST_TO_FLOAT64: return false; - - default: CUDF_UNREACHABLE("Unrecognized operator type."); + case opcode::IS_NULL: + case opcode::NULL_EQUAL: + case opcode::NULL_LOGICAL_AND: + case opcode::NULL_LOGICAL_OR: + case opcode::PREDICATE: return true; + + default: return false; } } -bool operation::is_null_aware() -{ - return is_operator_null_aware(op_) || - std::any_of( - operands_.begin(), operands_.end(), [](auto& op) { return op->is_null_aware(); }); -} +enum class [[nodiscard]] null_output : uint8_t { + PROPAGATE = 0, + ALWAYS_VALID = 1, + ALWAYS_NULLABLE = 2, +}; -inline bool is_operator_always_valid(opcode op) +[[nodiscard]] inline null_output get_op_null_output(opcode op) { switch (op) { - case ast::ast_operator::IS_NULL: - case ast::ast_operator::NULL_EQUAL: return true; - - case ast::ast_operator::NULL_LOGICAL_AND: - case ast::ast_operator::NULL_LOGICAL_OR: - case ast::ast_operator::ADD: - case ast::ast_operator::SUB: - case ast::ast_operator::MUL: - case ast::ast_operator::DIV: - case ast::ast_operator::TRUE_DIV: - case ast::ast_operator::FLOOR_DIV: - case ast::ast_operator::MOD: - case ast::ast_operator::PYMOD: - case ast::ast_operator::POW: - case ast::ast_operator::NOT_EQUAL: - case ast::ast_operator::EQUAL: - case ast::ast_operator::LESS: - case ast::ast_operator::GREATER: - case ast::ast_operator::LESS_EQUAL: - case ast::ast_operator::GREATER_EQUAL: - case ast::ast_operator::BITWISE_AND: - case ast::ast_operator::BITWISE_OR: - case ast::ast_operator::BITWISE_XOR: - case ast::ast_operator::LOGICAL_AND: - case ast::ast_operator::LOGICAL_OR: - case ast::ast_operator::IDENTITY: - case ast::ast_operator::SIN: - case ast::ast_operator::COS: - case ast::ast_operator::TAN: - case ast::ast_operator::ARCSIN: - case ast::ast_operator::ARCCOS: - case ast::ast_operator::ARCTAN: - case ast::ast_operator::SINH: - case ast::ast_operator::COSH: - case ast::ast_operator::TANH: - case ast::ast_operator::ARCSINH: - case ast::ast_operator::ARCCOSH: - case ast::ast_operator::ARCTANH: - case ast::ast_operator::EXP: - case ast::ast_operator::LOG: - case ast::ast_operator::SQRT: - case ast::ast_operator::CBRT: - case ast::ast_operator::CEIL: - case ast::ast_operator::FLOOR: - case ast::ast_operator::ABS: - case ast::ast_operator::RINT: - case ast::ast_operator::BIT_INVERT: - case ast::ast_operator::NOT: - case ast::ast_operator::CAST_TO_INT64: - case ast::ast_operator::CAST_TO_UINT64: - case ast::ast_operator::CAST_TO_FLOAT64: return false; - - default: CUDF_UNREACHABLE("Unrecognized operator type."); + case opcode::IS_NULL: + case opcode::NULL_EQUAL: + case opcode::PREDICATE: return null_output::ALWAYS_VALID; + + case opcode::NULL_LOGICAL_AND: + case opcode::NULL_LOGICAL_OR: return null_output::ALWAYS_NULLABLE; + + default: return null_output::PROPAGATE; } } -bool operation::is_always_valid() +bool node::is_null_aware() const { - return is_operator_always_valid(op_) || - std::all_of( - operands_.begin(), operands_.end(), [](auto& op) { return op->is_always_valid(); }); -} + if (op_ == opcode::GET_INPUT) { return false; } -opcode operation::get_opcode() const { return op_; } + // to emit nulls for always-nullable operators, we need to mark them as null-aware + if (get_op_null_output(op_) == null_output::ALWAYS_NULLABLE) { return true; } -std::span const> operation::get_operands() const { return operands_; } + if (get_op_requires_nulls(op_)) { return true; } -void operation::instantiate(instance_context& ctx, instance_info const& info) -{ - for (auto& arg : operands_) { - arg->instantiate(ctx, info); - } - - id_ = ctx.make_tmp_id(); - std::vector operand_types; + CUDF_EXPECTS(!args_.empty(), + "Unexpectedly found an operator node with no arguments. All operator nodes should " + "have at least one argument.", + std::runtime_error); - for (auto& arg : operands_) { - operand_types.emplace_back(arg->get_type()); - } - - type_ = ast::detail::ast_operator_return_type(op_, operand_types); + return std::any_of(args_.begin(), args_.end(), [](auto& a) { return a->is_null_aware(); }); } -std::string operation::generate_code(instance_context& ctx, - target_info const& info, - instance_info const& instance) +bool node::is_always_valid() const { - std::string operands_code; + if (op_ == opcode::GET_INPUT) { return false; } - for (auto& arg : operands_) { - operands_code = - std::format("{}{}{}", operands_code, arg->generate_code(ctx, info, instance), "\n"); - } + if (get_op_null_output(op_) == null_output::ALWAYS_VALID) { return true; } - auto operation_code = [&]() { - switch (info.id) { - case target::CUDA: { - auto first_operand = operands_[0]->get_id(); - auto operands_str = (operands_.size() == 1) - ? std::string{first_operand} - : std::accumulate(operands_.begin() + 1, - operands_.end(), - std::string{first_operand}, - [](auto const& a, auto& node) { - return std::format("{}, {}", a, node->get_id()); - }); - - auto cuda = std::format( - "{} {} = cudf::ast::detail::operator_functor{{}}({});", - cuda_type(type_, ctx.has_nulls()), - id_, - ast::detail::ast_operator_string(op_), - ctx.has_nulls(), - operands_str); - return cuda; - } - default: - CUDF_FAIL("Unsupported target: " + std::to_string(static_cast(info.id)), - std::invalid_argument); - } - }(); + CUDF_EXPECTS(!args_.empty(), + "Unexpectedly found an operator node with no arguments. All operator nodes should " + "have at least one argument.", + std::runtime_error); - return operands_code + operation_code; + return std::all_of(args_.begin(), args_.end(), [](auto& a) { return a->is_always_valid(); }); } -filter_predicate::filter_predicate(std::unique_ptr source) : id_(), source_(std::move(source)) +std::string to_cuda_type(cudf::data_type type, bool nullable) { + auto name = type_to_name(type); + return nullable ? std::format("cuda::std::optional<{}>", name) : name; } -std::string_view filter_predicate::get_id() { return id_; } - -data_type filter_predicate::get_type() { return data_type{type_id::BOOL8}; } - -bool filter_predicate::is_null_aware() { return source_->is_null_aware(); } +void node::instantiate(instance_context& ctx) +{ + for (auto& arg : args_) { + arg->instantiate(ctx); + } -bool filter_predicate::is_always_valid() { return true; } + id_ = ctx.make_tmp_id(); -node& filter_predicate::get_source() { return *source_; } + switch (op_) { + case opcode::GET_INPUT: { + type_ = ctx.get_input_vars()[std::get(reference_).index].type; + } break; + case opcode::SET_OUTPUT: { + type_ = args_[0]->get_type(); + } break; + case opcode::PREDICATE: { + type_ = data_type{type_id::BOOL8, 0}; + } break; + default: { + std::vector arg_types; + for (auto& arg : args_) { + arg_types.emplace_back(arg->get_type()); + } -void filter_predicate::instantiate(instance_context& ctx, instance_info const& info) -{ - source_->instantiate(ctx, info); - CUDF_EXPECTS(source_->get_type().id() == type_id::BOOL8, - "Filter predicate source must be boolean.", - std::invalid_argument); - id_ = ctx.make_tmp_id(); + type_ = ast::detail::ast_operator_return_type(as_ast_op(op_), arg_types); + } break; + } } -[[nodiscard]] std::string filter_predicate::generate_code(instance_context& ctx, - target_info const& info, - instance_info const& instance) +void node::emit_code(instance_context& instance, target_info const& info, code_sink& sink) const { + for (auto& arg : args_) { + arg->emit_code(instance, info, sink); + } + switch (info.id) { case target::CUDA: { - auto source_code = source_->generate_code(ctx, info, instance); - return std::format( - "{}\n" - "bool {} = cudf::ast::detail::flatten_predicate({});\n", - source_code, - id_, - source_->get_id()); - } + auto type = to_cuda_type(type_, instance.has_nulls()); + + switch (op_) { + case opcode::GET_INPUT: { + sink.emit( + std::format(R"***({} {} = {}; +)***", + type, + id_, + instance.get_input_vars()[std::get(reference_).index].id)); + } break; + + case opcode::SET_OUTPUT: { + sink.emit(std::format( + R"***({} {} = {}; +*{} = {}; +)***", + type, + id_, + args_[0]->get_id(), + instance.get_output_vars()[std::get(reference_).index].id, + id_)); + } break; + + default: { + CUDF_EXPECTS(op_ != opcode::RESCALE, "Rescale is not implemented", std::runtime_error); + + auto first_arg = std::format("{}", args_[0]->get_id()); + auto args_str = (args_.size() == 1) + ? std::string{first_arg} + : std::accumulate(args_.begin() + 1, + args_.end(), + std::string{first_arg}, + [](auto const& a, auto& node) { + return std::format("{}, {}", a, node->get_id()); + }); + + if (op_ == opcode::PREDICATE) { + sink.emit(std::format( + R"***(bool {} = cudf::ast::detail::predicate({}); +)***", + id_, + args_str)); + } else { + sink.emit(std::format( + R"***({} {} = cudf::ast::detail::operator_functor{{}}({}); +)***", + type, + id_, + ast::detail::ast_operator_string(as_ast_op(op_)), + instance.has_nulls(), + args_str)); + } + } break; + } + } break; + default: - CUDF_FAIL("Unsupported target: " + std::to_string(static_cast(info.id)), + CUDF_FAIL(std::format("Unsupported target: {}", static_cast(info.id)), std::invalid_argument); } } -std::span ast_converter::get_input_specs() const { return input_specs_; } - -int32_t ast_converter::add_ast_input(ast_input_spec in) -{ - auto id = static_cast(input_specs_.size()); - input_specs_.push_back(std::move(in)); - return id; -} - std::unique_ptr ast_converter::add_ir_node(ast::literal const& expr) { - auto index = add_ast_input( - ast_scalar_input_spec{expr.get_scalar(), - expr.get_value(), - make_column_from_scalar(expr.get_scalar(), 1, stream_, mr_)}); - return std::make_unique(index); + auto id = instance_.add_input(expr.get_scalar()); + return std::make_unique(input_reference{id}); } std::unique_ptr ast_converter::add_ir_node(ast::column_reference const& expr) { - auto index = - add_ast_input(ast_column_input_spec{expr.get_table_source(), expr.get_column_index()}); - return std::make_unique(index); -} - -std::unique_ptr ast_converter::add_ir_node(ast::operation const& expr) -{ - std::vector> operands; - for (auto const& operand : expr.get_operands()) { - operands.push_back(operand.get().accept(*this)); - } - return std::make_unique(expr.get_operator(), std::move(operands)); -} + // resolve the table for a column input spec, preferring left_table/right_table for join cases, + // falling back to args.table for the single-table case. + auto resolve = [&](ast::table_reference ref) { + CUDF_EXPECTS(ref == ast::table_reference::LEFT || ref == ast::table_reference::RIGHT, + "Invalid table reference in column expression", + std::invalid_argument); + return ref == ast::table_reference::LEFT ? left_table_ : right_table_; + }; -std::unique_ptr ast_converter::add_ir_node(ast::detail::filter_predicate const& expr) -{ - auto operand = expr.get_operand().accept(*this); - return std::make_unique(std::move(operand)); + auto table = resolve(expr.get_table_source()); + auto id = instance_.add_input( + column_input{.column = table.column(expr.get_column_index()), + .table_source = (expr.get_table_source() == ast::table_reference::LEFT ? 0 : 1), + .column_index = static_cast(expr.get_column_index())}); + return std::make_unique(input_reference{id}); } -// Resolve the table for a column input spec, preferring left_table/right_table for join cases, -// falling back to args.table for the single-table case. -table_view const& resolve_table(ast_column_input_spec const& in, ast_args const& args) +std::unique_ptr ast_converter::add_ir_node(ast::operation const& expr) { - if (in.table == ast::table_reference::LEFT) { - return args.left_table.num_columns() > 0 ? args.left_table : args.table; + std::vector> args; + for (auto& operand : expr.get_operands()) { + args.emplace_back(operand.get().accept(*this)); } - return args.right_table; + return std::make_unique( + as_opcode(expr.get_operator()), std::nullopt, std::move(args)); } -void ast_converter::add_input_var(ast_column_input_spec const& in, ast_args const& args) +std::unique_ptr ast_converter::add_ir_node(ast::detail::predicate const& expr) { - // TODO(lamarrr): consider mangling column name to make debugging easier - auto id = std::format("in_{}", input_vars_.size()); - auto type = resolve_table(in, args).column(in.column).type(); - input_vars_.emplace_back(std::move(id), type); + return std::make_unique( + row_ir::opcode::PREDICATE, std::nullopt, expr.get_operand().accept(*this)); } -void ast_converter::add_input_var(ast_scalar_input_spec const& in, - [[maybe_unused]] ast_args const& args) -{ - auto id = std::format("in_{}", input_vars_.size()); - auto type = in.ref.get().type(); - input_vars_.emplace_back(std::move(id), type); -} +bool is_nullable(scalar_input const& in) { return in.scalar_column->view().nullable(); } -void ast_converter::add_output_var() -{ - auto id = std::format("out_{}", output_vars_.size()); - output_vars_.emplace_back(std::move(id)); -} +bool is_nullable(column_input const& in) { return in.column.nullable(); } -template -decltype(auto) dispatch_input_spec(ast_input_spec const& in, Fn&& fn, Args&&... args) +std::tuple ast_converter::generate_code( + target target_id, ast::expression const& expr, std::string_view function_name) { - if (std::holds_alternative(in)) { - return fn(std::get(in), std::forward(args)...); - } else if (std::holds_alternative(in)) { - return fn(std::get(in), std::forward(args)...); - } else { - CUDF_FAIL("Unsupported input type"); - } -} - -std::variant get_column_view(ast_column_input_spec const& spec, - ast_args const& args) -{ - return resolve_table(spec, args).column(spec.column); -} - -std::variant get_column_view(ast_scalar_input_spec const& spec, - ast_args const& args) -{ - return scalar_column_view{spec.broadcast_column->view()}; -} + // add 1 auto-deduced output variable + [[maybe_unused]] auto output_id = instance_.add_output(); -std::tuple ast_converter::generate_code(target target_id, - ast::expression const& expr, - ast_args const& args) -{ - auto output_expr_ir = expr.accept(*this); - output_irs_.emplace_back(std::make_unique(0, std::move(output_expr_ir))); - - // resolve the flattened input references into IR input variables - for (auto const& input : input_specs_) { - dispatch_input_spec(input, [this](auto&... args) { add_input_var(args...); }, args); - } + output_irs_.emplace_back(std::make_unique(output_reference{0}, expr.accept(*this))); bool has_nullable_inputs = - std::any_of(input_specs_.begin(), input_specs_.end(), [&](auto const& input) { - return dispatch_input_spec( - input, - [](auto&... args) { - auto col = get_column_view(args...); - return std::visit([](auto& view) { return view.nullable(); }, col); - }, - args); + std::any_of(instance_.inputs_.begin(), instance_.inputs_.end(), [&](auto& in) { + return std::visit([](auto& c) { return is_nullable(c); }, in); }); - // add 1 auto-deduced output variable - add_output_var(); - - instance_context instance_ctx; - instance_info instance{input_vars_, output_vars_}; - - auto is_null_aware = - std::any_of( - output_irs_.cbegin(), output_irs_.cend(), [](auto& ir) { return ir->is_null_aware(); }) - ? null_aware::YES - : null_aware::NO; + bool is_null_aware = std::any_of( + output_irs_.cbegin(), output_irs_.cend(), [](auto& ir) { return ir->is_null_aware(); }); bool output_is_always_valid = std::all_of( output_irs_.cbegin(), output_irs_.cend(), [](auto& ir) { return ir->is_always_valid(); }); - bool may_evaluate_null = !output_is_always_valid && has_nullable_inputs; + bool may_evaluate_null = !output_is_always_valid || has_nullable_inputs; + auto null_policy = may_evaluate_null ? output_nullability::PRESERVE : output_nullability::ALL_VALID; - instance_ctx.set_has_nulls(is_null_aware == null_aware::YES); + instance_.set_has_nulls(is_null_aware); // instantiate the IR nodes for (auto& ir : output_irs_) { - ir->instantiate(instance_ctx, instance); + ir->instantiate(instance_); } target_info target{target_id}; - std::string body; + CUDF_EXPECTS( + target.id == target::CUDA, "Unsupported target for code generation", std::invalid_argument); - for (auto& ir : output_irs_) { - body = std::format("{}{}{}", body, ir->generate_code(instance_ctx, target, instance), "\n"); + auto output_decl = [&](auto i) { + auto& var = instance_.output_vars_[i]; + auto& ir = output_irs_[i]; + return std::format("{}* {}", to_cuda_type(ir->get_type(), instance_.has_nulls()), var.id); + }; + + auto input_decl = [&](auto i) { + auto& var = instance_.input_vars_[i]; + return std::format("{} {}", to_cuda_type(var.type, instance_.has_nulls()), var.id); + }; + + std::vector arg_decls; + + for (size_t i = 0; i < instance_.output_vars_.size(); ++i) { + arg_decls.emplace_back(output_decl(i)); } - switch (target.id) { - case target::CUDA: { - { - auto output_decl = [&](size_t i) { - auto const& var = output_vars_[i]; - auto const& ir = output_irs_[i]; - auto output_type = ir->get_type(); - return std::format("{}* {}", cuda_type(output_type, instance_ctx.has_nulls()), var.id); - }; - - auto input_decl = [&](size_t i) { - auto const& var = input_vars_[i]; - return std::format("{} {}", cuda_type(var.type, instance_ctx.has_nulls()), var.id); - }; - - std::vector params_decls; - - for (size_t i = 0; i < output_vars_.size(); ++i) { - params_decls.push_back(output_decl(i)); - } - - for (size_t i = 0; i < input_vars_.size(); ++i) { - params_decls.push_back(input_decl(i)); - } - - auto params_decl = [&] { - if (params_decls.empty()) { - return std::string{}; - } else if (params_decls.size() == 1) { - return params_decls[0]; - } else { - return std::accumulate( - params_decls.begin() + 1, - params_decls.end(), - params_decls[0], - [](auto const& a, auto const& b) { return std::format("{}, {}", a, b); }); - } - }(); - - code_ = std::format( - R"***( -__device__ void expression({}) -{{ -{} -return; -}} -)***", - params_decl, - body); + for (size_t i = 0; i < instance_.input_vars_.size(); ++i) { + arg_decls.emplace_back(input_decl(i)); + } - return {is_null_aware, null_policy}; - } - break; + auto args_decl = [&] { + if (arg_decls.empty()) { + return std::string{}; + } else if (arg_decls.size() == 1) { + return arg_decls[0]; + } else { + return std::accumulate( + arg_decls.begin() + 1, arg_decls.end(), arg_decls[0], [](auto const& a, auto const& b) { + return std::format("{}, {}", a, b); + }); } - default: - CUDF_FAIL("Unsupported target: " + std::to_string(static_cast(target.id)), - std::invalid_argument); + }(); + + code_sink sink; + sink.emit(std::format("__device__ void {}(", function_name)); + sink.emit(args_decl); + sink.emit(")\n{\n"); + for (auto& ir : output_irs_) { + ir->emit_code(instance_, target, sink); } + sink.emit("return;\n}"); + return {sink.get_code(), is_null_aware ? null_aware::YES : null_aware::NO, null_policy}; +} + +std::variant get_column_view(scalar_input const& in) +{ + return scalar_column_view{in.scalar_column->view()}; +} + +std::variant get_column_view(column_input const& in) +{ + return column_view{in.column}; } // Due to the AST expression tree structure, we can't generate the IR without the target // tables transform_args ast_converter::compute_column(target target_id, ast::expression const& expr, - ast_args const& args, + table_view const& left_table, + table_view const& right_table, + std::string_view function_name, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) { - ast_converter converter{stream, mr}; + ast_converter converter{stream, mr, left_table, right_table}; // TODO(lamarrr): consider deduplicating ast expression's input column references. See // TransformTest/1.DeeplyNestedArithmeticLogicalExpression for reference - auto [is_null_aware, output_nullability] = converter.generate_code(target_id, expr, args); - + auto [code, is_null_aware, output_nullability] = + converter.generate_code(target_id, expr, function_name); std::vector> inputs; std::vector> scalar_columns; + std::vector> table_sources; + std::vector> column_indices; + + for (auto& input : converter.instance_.inputs_) { + if (std::holds_alternative(input)) { + auto& col = std::get(input); + table_sources.emplace_back(col.table_source); + column_indices.emplace_back(col.column_index); + } else { + table_sources.emplace_back(std::nullopt); + column_indices.emplace_back(std::nullopt); + } - for (auto& input : converter.input_specs_) { - auto column_view = - dispatch_input_spec(input, [](auto&... args) { return get_column_view(args...); }, args); - inputs.emplace_back(column_view); + auto view = std::visit([](auto& in) { return get_column_view(in); }, input); + inputs.emplace_back(view); - if (std::holds_alternative(input)) { - auto& scalar_input = std::get(input); - scalar_columns.push_back(std::move(scalar_input.broadcast_column)); + if (std::holds_alternative(input)) { + auto& scalar = std::get(input); + scalar_columns.emplace_back(std::move(scalar.scalar_column)); } } auto& out = converter.output_irs_[0]; auto output_column_type = out->get_type(); - - auto result = transform_args{.scalar_columns = std::move(scalar_columns), - .inputs = inputs, - .udf = std::move(converter.code_), - .output_type = output_column_type, - .source_type = cudf::udf_source_type::CUDA, - .user_data = std::nullopt, - .is_null_aware = is_null_aware, - .null_policy = output_nullability, - .row_size = args.table.num_rows(), - .input_specs = std::move(converter.input_specs_)}; - + auto output = transform_output{.type = output_column_type, .nullability = output_nullability}; + auto row_size = std::max({left_table.num_rows(), right_table.num_rows()}); + auto result = transform_args{.scalar_columns = std::move(scalar_columns), + .input_table_sources = std::move(table_sources), + .input_column_indices = std::move(column_indices), + .udf = std::move(code), + .source_type = cudf::udf_source_type::CUDA, + .is_null_aware = is_null_aware, + .user_data = std::nullopt, + .inputs = inputs, + .outputs{output}, + .string_offsets{}, + .row_size = row_size}; if (get_context().dump_codegen()) { - std::cout << "Generated code for transform: " << result.udf << std::endl; + std::cout << "Generated code for transform: \n" << result.udf << std::endl; } return result; } -filter_args ast_converter::filter(target target_id, - ast::expression const& expr, - ast_args const& args, - table_view const& filter_table, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) +transform_args ast_converter::filter(target target_id, + ast::expression const& expr, + table_view const& left_table, + table_view const& right_table, + std::string_view function_name, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { - auto filter = ast::detail::filter_predicate{expr}; - auto transform = compute_column(target_id, filter, args, stream, mr); + auto filter = ast::detail::predicate{expr}; + auto transform = + compute_column(target_id, filter, left_table, right_table, function_name, stream, mr); - CUDF_EXPECTS(transform.output_type.id() == type_id::BOOL8, + CUDF_EXPECTS(transform.outputs.size() == 1, + "Filter expression must have exactly one output column.", + std::invalid_argument); + CUDF_EXPECTS(transform.outputs[0].type.id() == type_id::BOOL8, "Filter expression must return a boolean type.", std::invalid_argument); - std::vector filter_columns; - std::transform(filter_table.begin(), - filter_table.end(), - std::back_inserter(filter_columns), - [](auto const& col) { return col; }); - - auto result = filter_args{.scalar_columns = std::move(transform.scalar_columns), - .inputs = std::move(transform.inputs), - .filter_columns = std::move(filter_columns), - .udf = std::move(transform.udf), - .source_type = transform.source_type, - .user_data = transform.user_data, - .is_null_aware = transform.is_null_aware, - .predicate_nullability = transform.null_policy, - .input_specs = std::move(transform.input_specs)}; - - return result; + return transform; } -} // namespace row_ir -} // namespace detail -} // namespace cudf +} // namespace cudf::detail::row_ir diff --git a/cpp/src/jit/row_ir.hpp b/cpp/src/jit/row_ir.hpp index 5e3cd2633e68..4a7da1430014 100644 --- a/cpp/src/jit/row_ir.hpp +++ b/cpp/src/jit/row_ir.hpp @@ -6,7 +6,9 @@ #pragma once #include #include +#include #include +#include #include #include #include @@ -15,10 +17,7 @@ #include #include -#include -#include #include -#include #include #include #include @@ -60,18 +59,40 @@ struct untyped_var_info { }; /** - * @brief The information needed to instantiate the IR nodes + * @brief The information about the target for which the IR is generated. */ -struct instance_info { - std::span inputs; ///< The input variables - std::span outputs; ///< The output variables +struct target_info { + target id = target::CUDA; ///< The target identifier +}; + +struct scalar_input { + std::unique_ptr scalar_column = + nullptr; ///< The scalar value represented as a column with a single element +}; + +struct column_input { + column_view column = {}; ///< The column input + std::optional table_source = std::nullopt; + std::optional column_index = std::nullopt; }; +using input = std::variant; + /** - * @brief The information about the target for which the IR is generated. + * @brief The arguments needed to invoke a `cudf::transform` */ -struct target_info { - target id = target::CUDA; ///< The target identifier +struct [[nodiscard]] transform_args { + std::vector> scalar_columns = {}; + std::vector> input_table_sources = {}; + std::vector> input_column_indices = {}; + std::string udf = {}; + udf_source_type source_type = cudf::udf_source_type::CUDA; + null_aware is_null_aware = null_aware::NO; + std::optional user_data = std::nullopt; + std::vector inputs = {}; + std::vector outputs = {}; + std::vector> string_offsets = {}; + std::optional row_size = std::nullopt; }; /** @@ -81,12 +102,25 @@ struct target_info { */ struct [[nodiscard]] instance_context { private: - int32_t num_tmp_vars_ = 0; ///< The number of temporary variables generated - std::string tmp_prefix_ = "tmp_"; ///< The prefix for temporary variable identifiers - bool has_nulls_ = false; ///< If expressions involve null values + int32_t num_tmp_vars_ = 0; ///< The number of temporary variables generated + std::string tmp_prefix_ = "tmp_"; ///< The prefix for temporary variable identifiers + bool has_nulls_ = false; ///< If expressions involve null values + std::vector inputs_; ///< The inputs for the IR + std::vector input_vars_; ///< The input variables for the IR + std::vector output_vars_; ///< The output variables for the IR + rmm::cuda_stream_view + stream_; ///< The CUDA stream for any device operations during IR generation + rmm::device_async_resource_ref + mr_; ///< The device memory resource for any device memory allocation during IR generation public: - instance_context() = default; ///< Default constructor + friend struct ast_converter; + friend struct node; + + instance_context(rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) + : stream_(stream), mr_(mr) + { + } instance_context(instance_context const&) = delete; @@ -98,6 +132,21 @@ struct [[nodiscard]] instance_context { ~instance_context() = default; ///< Destructor + [[nodiscard]] int32_t add_output(); + + [[nodiscard]] int32_t add_input(input in); + + [[nodiscard]] int32_t add_input(scalar const& scalar) + { + return add_input( + scalar_input{.scalar_column = make_column_from_scalar(scalar, 1, stream_, mr_)}); + } + + [[nodiscard]] int32_t add_input(column_view const& column) + { + return add_input(column_input{.column = column}); + } + /** * @brief Generate a globally unique temporary variable identifier * @return A unique temporary variable identifier @@ -114,406 +163,258 @@ struct [[nodiscard]] instance_context { * @param has_nulls True if expressions involve null values */ void set_has_nulls(bool has_nulls); -}; -struct [[nodiscard]] node { /** - * @brief Get the identifier of the IR node - * @return The identifier of the IR node - */ - virtual std::string_view get_id() = 0; - - /** - * @brief Get the type info of the IR node - * @return The type information of the IR node + * @brief Get the input values for the IR + * @return A span of input values for the IR */ - [[nodiscard]] virtual data_type get_type() = 0; + [[nodiscard]] std::span get_inputs() const; /** - * @brief Returns `false` if this node forwards nulls from its inputs to its output. - * e.g., `ADD` operator is not null-aware because if any of its inputs is null, the output is - * null. but `NULL_EQUAL` operator is null-aware because it can produce a non-null output even if - * its inputs are null. + * @brief Get the input variables for the IR + * @return A span of input variable information */ - [[nodiscard]] virtual bool is_null_aware() = 0; + [[nodiscard]] std::span get_input_vars() const; /** - * @brief Returns `true` if this node always produces a valid output even if its inputs are - * nullable, e.g., `IS_NULL` operator produces a valid boolean output regardless of the - * nullability of its input. + * @brief Get the output variables for the IR + * @return A span of output variable information */ - [[nodiscard]] virtual bool is_always_valid() = 0; - - /** - * @brief Instantiate the IR node with the given context and instance information, setting up any - * necessary state and preprocessing needed for code generation. - * @param ctx The context within which the IR is instantiated - * @param info The instance information - */ - virtual void instantiate(instance_context& ctx, instance_info const& info) = 0; - - /** - * @brief Generate the code for the IR node based on the instance context and target information. - * @param ctx The context within which the IR is instantiated - * @param info The target information - * @param instance The instance information - * @return The generated code for the IR node - */ - [[nodiscard]] virtual std::string generate_code(instance_context& ctx, - target_info const& info, - instance_info const& instance) = 0; - - virtual ~node() = default; + [[nodiscard]] std::span get_output_vars() const; }; -/** - * @brief The operation code used in the IR nodes. - */ -using opcode = ast::ast_operator; - -/** - * @brief An IR node that retrieves an input variable by its index. - * This node is used to access input variables in the IR. - */ -struct [[nodiscard]] get_input final : node { +struct [[nodiscard]] code_sink { private: - std::string id_; ///< The identifier of the IR node - int32_t input_; ///< The index of the input variable - data_type type_; ///< The type information of the IR node + std::string code_; public: - /** - * @brief Construct a new get_input IR node - * @param input The index of the input variable - */ - get_input(int32_t input); - - get_input(get_input const&) = delete; - - get_input& operator=(get_input const&) = delete; - - get_input(get_input&&) = default; ///< Move constructor + void emit(std::string_view code) { code_ += code; } - get_input& operator=(get_input&&) = default; ///< Move assignment operator - - ~get_input() override = default; ///< Destructor - - /** - * @copydoc node::get_id - */ - [[nodiscard]] std::string_view get_id() override; - - /** - * @copydoc node::get_type - */ - [[nodiscard]] data_type get_type() override; - - /** - * @copydoc node::is_null_aware - */ - [[nodiscard]] bool is_null_aware() override; - - /** - * @copydoc node::is_always_valid - */ - [[nodiscard]] bool is_always_valid() override; + [[nodiscard]] std::string const& get_code() const { return code_; } +}; - /** - * @copydoc node::instantiate - */ - void instantiate(instance_context& ctx, instance_info const& info) override; +struct [[nodiscard]] input_reference { + int32_t index = 0; ///< The index of the input variable +}; - /** - * @copydoc node::generate_code - */ - [[nodiscard]] std::string generate_code(instance_context& ctx, - target_info const& info, - instance_info const& instance) override; +struct [[nodiscard]] output_reference { + int32_t index = 0; ///< The index of the output variable }; /** - * @brief An IR node that sets the output variable to the value of a source IR node. + * @brief The operation code used in the IR nodes. */ -struct [[nodiscard]] set_output final : node { - private: - std::string id_; ///< The identifier of the IR node - int32_t output_; ///< The index of the output variable - std::unique_ptr source_; ///< The source IR node from which the value is taken - data_type type_; ///< The type information of the IR node - std::string output_id_; ///< The identifier of the output variable - - public: - /** - * @brief Construct a new set_output IR node - * @param output The index of the output variable - * @param source The source IR node from which the value is taken - */ - set_output(int32_t output, std::unique_ptr source); - - set_output(set_output const&) = delete; - - set_output& operator=(set_output const&) = delete; - - set_output(set_output&&) = default; ///< Move constructor - - set_output& operator=(set_output&&) = default; ///< Move assignment operator - - ~set_output() override = default; ///< Destructor - - /** - * @copydoc node::get_id - */ - [[nodiscard]] std::string_view get_id() override; - - /** - * @copydoc node::get_type - */ - [[nodiscard]] data_type get_type() override; - - /** - * @copydoc node::is_null_aware - */ - [[nodiscard]] bool is_null_aware() override; - - /** - * @copydoc node::is_always_valid - */ - [[nodiscard]] bool is_always_valid() override; - - /** - * @brief Get the source IR node from which the value is taken - */ - [[nodiscard]] node& get_source(); - - /** - * @copydoc node::instantiate - */ - void instantiate(instance_context& ctx, instance_info const& info) override; - - /** - * @copydoc node::generate_code - */ - [[nodiscard]] std::string generate_code(instance_context& ctx, - target_info const& info, - instance_info const& instance) override; +enum class opcode : int32_t { + GET_INPUT, + SET_OUTPUT, + PREDICATE, + RESCALE, + ADD, + SUB, + MUL, + DIV, + TRUE_DIV, + FLOOR_DIV, + MOD, + PYMOD, + POW, + EQUAL, + NULL_EQUAL, + NOT_EQUAL, + LESS, + GREATER, + LESS_EQUAL, + GREATER_EQUAL, + BITWISE_AND, + BITWISE_OR, + BITWISE_XOR, + LOGICAL_AND, + NULL_LOGICAL_AND, + LOGICAL_OR, + NULL_LOGICAL_OR, + IDENTITY, + IS_NULL, + SIN, + COS, + TAN, + ARCSIN, + ARCCOS, + ARCTAN, + SINH, + COSH, + TANH, + ARCSINH, + ARCCOSH, + ARCTANH, + EXP, + LOG, + SQRT, + CBRT, + CEIL, + FLOOR, + ABS, + RINT, + BIT_INVERT, + NOT, + CAST_TO_INT64, + CAST_TO_UINT64, + CAST_TO_FLOAT64 }; -/** - * @brief An IR node that represents an operation with zero or more operands. - */ -struct [[nodiscard]] operation final : node { +struct [[nodiscard]] node { private: - std::string id_; ///< The identifier of the IR node - opcode op_; ///< The operation code - std::vector> operands_; ///< The operands of the operation - data_type type_; ///< The type information of the IR node + std::variant reference_ = + std::monostate{}; ///< The index of the input/output variable + opcode op_ = opcode::GET_INPUT; ///< The operation code + std::optional target_scale_ = std::nullopt; ///< The target scale for decimal + std::vector> args_ = {}; ///< The arguments of the operation - operation(opcode op, std::unique_ptr* move_begin, std::unique_ptr* move_end); + data_type type_ = {}; ///< The resolved type information of the IR node + + std::string id_ = {}; ///< The identifier of the IR node - public: /** - * @brief Create a set of operand IR nodes + * @brief Create a set of argument IR nodes */ template - requires(std::is_base_of_v && ...) - static std::array, sizeof...(T)> operands(T&&... args) + requires(std::is_same_v && ...) + static std::vector> arguments(T... args) { - return {std::make_unique(std::forward(args))...}; + std::vector> result; + (result.emplace_back(std::make_unique(std::move(args))), ...); + return result; } /** - * @brief Create a set of operand IR nodes from existing unique pointers + * @brief Create a set of argument IR nodes */ template - requires(std::is_base_of_v && ...) - static std::array, sizeof...(T)> operands(std::unique_ptr&&... args) + requires(std::is_same_v, T> && ...) + static std::vector> arguments(T... args) { - return {std::move(args)...}; + std::vector> result; + (result.emplace_back(std::move(args)), ...); + return result; } + public: /** * @brief Construct a new operation IR node * @param op The operation code - * @param operands The operands of the operation + * @param args The arguments of the operation */ - operation(opcode op, std::vector> operands); - - template - operation(opcode op, std::array, N> operands) - : operation{op, operands.data(), operands.data() + N} - { - } - - operation(operation const&) = delete; - - operation& operator=(operation const&) = delete; - - operation(operation&&) = default; ///< Move constructor - - operation& operator=(operation&&) = default; ///< Move assignment operator - - ~operation() override = default; ///< Destructor + node(opcode op, std::optional target_scale, std::vector> args); /** - * @copydoc node::get_id + * @brief Construct a new operation IR node + * @param op The operation code + * @param args The arguments of the operation */ - [[nodiscard]] std::string_view get_id() override; + template + requires(std::is_same_v && ...) + node(opcode op, std::optional target_scale, T... args) + : node(op, target_scale, arguments(std::move(args)...)) + { + } /** - * @copydoc node::get_type + * @brief Construct a new operation IR node + * @param op The operation code + * @param args The arguments of the operation */ - [[nodiscard]] data_type get_type() override; + template + requires(std::is_same_v && ...) + node(opcode op, std::optional target_scale, std::unique_ptr... args) + : node(op, target_scale, arguments(std::move(args)...)) + { + } /** - * @copydoc node::is_null_aware + * @brief Construct a new input reference IR node + * @param input The index of the input variable */ - [[nodiscard]] bool is_null_aware() override; + node(input_reference input); /** - * @copydoc node::is_always_valid + * @brief Construct a new output reference IR node + * @param output The index of the output variable + * @param arg The argument node that produces the value to be set to the output variable */ - [[nodiscard]] bool is_always_valid() override; + node(output_reference reference, std::unique_ptr arg); /** - * @brief Get the operation code of the operation - * @return The operation code of the operation + * @brief Construct a new output reference IR node + * @param output The index of the output variable + * @param arg The argument node that produces the value to be set to the output variable */ - [[nodiscard]] opcode get_opcode() const; + node(output_reference reference, node arg); - /** @brief Get the operands of the operation - * @return A span of unique pointers to the operands of the operation - */ - [[nodiscard]] std::span const> get_operands() const; + node(node const& other) = delete; + node(node&& other) = default; ///< Move constructor + node& operator=(node const& other) = delete; + node& operator=(node&& other) = default; ///< Move assignment operator + ~node() = default; ///< Destructor /** - * @copydoc node::instantiate + * @brief Get the identifier of the IR node + * @return The identifier of the IR node */ - void instantiate(instance_context& ctx, instance_info const& info) override; + [[nodiscard]] std::string_view get_id() const; /** - * @copydoc node::generate_code + * @brief Get the type info of the IR node + * @return The type information of the IR node */ - [[nodiscard]] std::string generate_code(instance_context& ctx, - target_info const& info, - instance_info const& instance) override; -}; - -/** - * @brief An IR node that flattens a boolean predicate to be used in a filter operation. - * This node replaces null values with false. - */ -struct [[nodiscard]] filter_predicate final : node { - private: - std::string id_; ///< The identifier of the IR node - std::unique_ptr source_; ///< The source IR node from which the predicate value is taken - - public: - filter_predicate(std::unique_ptr source); + [[nodiscard]] data_type get_type() const; /** - * @copydoc node::get_id + * @brief Get the target scale for decimal rescaling if applicable + * @return The target scale for decimal rescaling if applicable, std::nullopt otherwise */ - [[nodiscard]] std::string_view get_id() override; + [[nodiscard]] std::optional get_target_scale() const; /** - * @copydoc node::get_type + * @brief Get the operation code of the operation + * @return The operation code of the operation */ - [[nodiscard]] data_type get_type() override; + [[nodiscard]] opcode get_opcode() const; - /** - * @copydoc node::is_null_aware + /** @brief Get the arguments of the operation + * @return A span of unique pointers to the arguments of the operation */ - [[nodiscard]] bool is_null_aware() override; + [[nodiscard]] std::span const> get_args() const; /** - * @copydoc node::is_always_valid + * @brief Returns `false` if this node forwards nulls from its inputs to its output. + * e.g., `ADD` operator is not null-aware because if any of its inputs is null, the output is + * null. but `NULL_EQUAL` operator is null-aware because it can produce a non-null output even if + * its inputs are null. */ - [[nodiscard]] bool is_always_valid() override; + [[nodiscard]] bool is_null_aware() const; /** - * @brief Get the source IR node from which the value is taken + * @brief Returns `true` if this node always produces a valid output even if its inputs are + * nullable, e.g., `IS_NULL` operator produces a valid boolean output regardless of the + * nullability of its input. */ - [[nodiscard]] node& get_source(); + [[nodiscard]] bool is_always_valid() const; /** - * @copydoc node::instantiate + * @brief Instantiate the IR node with the given context and instance information, setting up any + * necessary state and preprocessing needed for code generation. + * @param ctx The context within which the IR is instantiated + * @param info The instance information */ - void instantiate(instance_context& ctx, instance_info const& info) override; + void instantiate(instance_context& ctx); /** - * @copydoc node::generate_code + * @brief Generate the code for the IR node based on the instance context and target information. + * @param ctx The context within which the IR is instantiated + * @param info The target information + * @param instance The instance information + * @param sink The code sink to which the generated code is emitted */ - [[nodiscard]] std::string generate_code(instance_context& ctx, - target_info const& info, - instance_info const& instance) override; -}; - -/** - * @brief A specification of an input column to the AST - */ -struct ast_column_input_spec { - ast::table_reference table = {}; ///< The table reference (LEFT or RIGHT) - int32_t column = 0; ///< The column index in the referenced table -}; - -/** - * @brief A specification of an input scalar to the AST - */ -struct ast_scalar_input_spec { - std::reference_wrapper ref; ///< The scalar value - ast::generic_scalar_device_view view; ///< The device view of the scalar value - std::unique_ptr broadcast_column = - nullptr; ///< The broadcasted column, a column of size 1 -}; - -/** - * @brief An input specification for the AST - */ -using ast_input_spec = std::variant; - -/** - * @brief The arguments needed to invoke a `cudf::transform` - */ -struct [[nodiscard]] transform_args { - std::vector> scalar_columns = - {}; ///< The scalar columns created during the expression conversion - std::vector> inputs = - {}; ///< The input columns to the transform UDF - std::string udf = {}; ///< The user-defined function to apply - data_type output_type = data_type{type_id::EMPTY}; ///< The output type of the transform - cudf::udf_source_type source_type = cudf::udf_source_type::CUDA; ///< The source type of the UDF - std::optional user_data = std::nullopt; ///< User data to pass to the transform - null_aware is_null_aware = null_aware::NO; ///< Whether the transform is null-aware - output_nullability null_policy = output_nullability::PRESERVE; ///< Null-transformation policy - std::optional row_size = std::nullopt; ///< The row size of the transform operation - std::vector input_specs = {}; ///< The input specs (table ref + column index) -}; - -/** - * @brief The arguments needed to invoke a `cudf::filter` - */ -struct [[nodiscard]] filter_args { - std::vector> scalar_columns = - {}; ///< The scalar columns created during the expression conversion - std::vector> inputs = - {}; ///< The input columns to the transform UDF - std::vector filter_columns = {}; ///< The input columns to the filter - std::string udf = {}; ///< The user-defined function to apply as a predicate - cudf::udf_source_type source_type = cudf::udf_source_type::CUDA; ///< The source type of the UDF - std::optional user_data = std::nullopt; ///< User data to pass to the filter - null_aware is_null_aware = null_aware::NO; ///< Whether the filter is null-aware - output_nullability predicate_nullability = - output_nullability::PRESERVE; ///< Null-transformation policy for the predicate output - std::vector input_specs = {}; ///< The input specs (table ref + column index) -}; - -/** - * @brief The AST input column arguments used to resolve the column expressions - */ -struct ast_args { - table_view table = {}; ///< The table view containing the columns (single-table case) - table_view left_table = {}; ///< The left table for join predicates - table_view right_table = {}; ///< The right table for join predicates + void emit_code(instance_context& ctx, target_info const& info, code_sink& sink) const; }; /** @@ -521,15 +422,14 @@ struct ast_args { */ struct [[nodiscard]] ast_converter { private: - std::vector input_specs_; ///< The input specs for the AST - std::vector input_vars_; ///< The input variables for the IR - std::vector output_vars_; ///< The output variables for the IR - std::vector> output_irs_; ///< The output IR nodes - std::string code_; ///< The generated code for the IR + std::vector> output_irs_; ///< The output IR nodes rmm::cuda_stream_view stream_; ///< CUDA stream used for device memory operations and kernel launches. rmm::device_async_resource_ref mr_; ///< Device memory resource used to allocate the returned table's device memory + instance_context instance_; ///< The instance context used during the IR generation + table_view left_table_; ///< The left input table for the expression + table_view right_table_; ///< The right input table for the expression public: /** @@ -537,8 +437,15 @@ struct [[nodiscard]] ast_converter { * @param stream CUDA stream used for device memory operations and kernel launches. * @param mr Device memory resource used to allocate the returned table's device memory */ - ast_converter(rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) - : stream_(std::move(stream)), mr_(std::move(mr)) + ast_converter(rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr, + table_view left_table, + table_view right_table) + : stream_(std::move(stream)), + mr_(std::move(mr)), + instance_(stream_, mr_), + left_table_(std::move(left_table)), + right_table_(std::move(right_table)) { } @@ -551,51 +458,35 @@ struct [[nodiscard]] ast_converter { ~ast_converter() = default; ///< Destructor - private: - friend class ast::literal; - friend class ast::column_reference; - friend class ast::operation; - friend class ast::column_name_reference; - friend class ast::detail::filter_predicate; - + public: [[nodiscard]] std::unique_ptr add_ir_node(ast::literal const& expr); [[nodiscard]] std::unique_ptr add_ir_node(ast::column_reference const& expr); [[nodiscard]] std::unique_ptr add_ir_node(ast::operation const& expr); - [[nodiscard]] std::unique_ptr add_ir_node( - ast::detail::filter_predicate const& expr); + [[nodiscard]] std::unique_ptr add_ir_node(ast::detail::predicate const& expr); - [[nodiscard]] std::span get_input_specs() const; + [[nodiscard]] std::tuple generate_code( + target target, ast::expression const& expr, std::string_view function_name); - /** - * @brief add an AST input/input_reference and return its reference index - */ - [[nodiscard]] int32_t add_ast_input(ast_input_spec in); - - void add_input_var(ast_column_input_spec const& in, ast_args const& args); - - void add_input_var(ast_scalar_input_spec const& in, ast_args const& args); - - void add_output_var(); - - [[nodiscard]] std::tuple generate_code( - target target, ast::expression const& expr, ast_args const& args); - - public: /** * @brief Convert an AST `compute_column` expression to a `cudf::transform` * @param target The target for which the IR is generated * @param expr The AST expression to convert - * @param args The arguments needed to resolve the AST expression + * @param left_table The left input table for the expression + * @param right_table The right input table for the expression + * @param table The input table for the expression + * @param function_name The name of the generated function * @param stream CUDA stream used for device memory operations and kernel launches. * @param mr Device memory resource used to allocate the returned table's device memory * @return The result of the conversion, containing the transform arguments and scalar columns */ static transform_args compute_column(target target, ast::expression const& expr, - ast_args const& args, + table_view const& left_table, + table_view const& right_table, + std::string_view function_name, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); @@ -603,18 +494,21 @@ struct [[nodiscard]] ast_converter { * @brief Convert an AST `filter` expression to a `cudf::filter` * @param target The target for which the IR is generated * @param expr The AST expression to convert - * @param args The arguments needed to resolve the AST expression - * @param filter_table The table to be filtered + * @param left_table The left input table for the expression + * @param right_table The right input table for the expression + * @param table The input table for the expression + * @param function_name The name of the generated function * @param stream CUDA stream used for device memory operations and kernel launches. * @param mr Device memory resource used to allocate the returned table's device memory * @return The result of the conversion, containing the filter arguments and scalar columns */ - static filter_args filter(target target, - ast::expression const& expr, - ast_args const& args, - table_view const& filter_table, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr); + static transform_args filter(target target, + ast::expression const& expr, + table_view const& left_table, + table_view const& right_table, + std::string_view function_name, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); }; } // namespace row_ir diff --git a/cpp/src/join/filter_join_indices_jit.cu b/cpp/src/join/filter_join_indices_jit.cu index d7a2565df341..f1d1d3c2b9cf 100644 --- a/cpp/src/join/filter_join_indices_jit.cu +++ b/cpp/src/join/filter_join_indices_jit.cu @@ -48,42 +48,49 @@ namespace detail { namespace { -// Build template parameters for JIT kernel -jitify2::StringVec build_join_filter_template_params(std::vector const& left_columns, - std::vector const& right_columns, - bool has_user_data, - null_aware is_null_aware) +jitify2::StringVec build_join_filter_template_params( + std::span inputs, + std::span> table_sources, + null_aware is_null_aware) { jitify2::StringVec template_params; - - template_params.emplace_back(jitify2::reflection::reflect(has_user_data)); + template_params.emplace_back(jitify2::reflection::reflect(false)); // has_user_data = false template_params.emplace_back(jitify2::reflection::reflect(is_null_aware)); - // Add left column accessors - for (std::size_t i = 0; i < left_columns.size(); ++i) { - auto const& col = left_columns[i]; - std::string type_name = cudf::type_to_name(col.type()); - template_params.emplace_back( - jitify2::reflection::Template("cudf::jit::join_column_accessor") - .instantiate(type_name, std::to_string(i), "cudf::jit::join_side::LEFT")); + jitify2::StringVec accessors; + + for (size_t i = 0; i < inputs.size(); ++i) { + auto const& input = inputs[i]; + if (auto* col = std::get_if(&input)) { + auto element = cudf::type_to_name(col->type()); + accessors.emplace_back( + jitify2::reflection::Template("cudf::jit::column_accessor") + .instantiate( + i, "cudf::column_device_view_core", element, false, table_sources[i].value())); + } else { + auto& scalar = std::get(input); + auto element = cudf::type_to_name(scalar.as_column_view().type()); + accessors.emplace_back(jitify2::reflection::Template("cudf::jit::column_accessor") + .instantiate( + i, + "cudf::column_device_view_core", + element, + true, + 0 // scalars dont belong to a table, so just use 0 as placeholder + )); + } } - // Add right column accessors - for (std::size_t i = 0; i < right_columns.size(); ++i) { - auto const& col = right_columns[i]; - std::string type_name = cudf::type_to_name(col.type()); - template_params.emplace_back( - jitify2::reflection::Template("cudf::jit::join_column_accessor") - .instantiate(type_name, std::to_string(i), "cudf::jit::join_side::RIGHT")); - } + template_params.push_back( + jitify2::reflection::Template("cudf::jit::type_list").instantiate(accessors)); return template_params; } // Build the JIT kernel for join filtering jitify2::ConfiguredKernel build_join_filter_kernel(std::string const& predicate_code, - std::vector const& left_columns, - std::vector const& right_columns, + std::span inputs, + std::span> table_sources, bool is_ptx, bool has_user_data, null_aware is_null_aware, @@ -92,24 +99,28 @@ jitify2::ConfiguredKernel build_join_filter_kernel(std::string const& predicate_ { CUDF_FUNC_RANGE(); + std::vector ptx_output_types{"bool"}; + std::vector ptx_input_types; + + for (auto const& input : inputs) { + if (auto* col = std::get_if(&input)) { + ptx_input_types.push_back(cudf::type_to_name(col->type())); + } else { + auto& scalar = std::get(input); + ptx_input_types.push_back(cudf::type_to_name(scalar.type())); + } + } + // Parse predicate code auto const cuda_source = is_ptx ? cudf::jit::parse_single_function_ptx( predicate_code, "GENERIC_JOIN_FILTER_OP", - [&] { - std::vector left_types, right_types; - for (auto const& col : left_columns) - left_types.push_back(cudf::type_to_name(col.type())); - for (auto const& col : right_columns) - right_types.push_back(cudf::type_to_name(col.type())); - return cudf::jit::build_ptx_params(left_types, right_types, has_user_data); - }()) + cudf::jit::build_ptx_params(ptx_output_types, ptx_input_types, has_user_data)) : cudf::jit::parse_single_function_cuda(predicate_code, "GENERIC_JOIN_FILTER_OP"); // Build template parameters and kernel name - auto template_args = - build_join_filter_template_params(left_columns, right_columns, has_user_data, is_null_aware); + auto template_args = build_join_filter_template_params(inputs, table_sources, is_null_aware); auto kernel_name = jitify2::reflection::Template("cudf::join::jit::filter_join_kernel").instantiate(template_args); @@ -122,44 +133,45 @@ jitify2::ConfiguredKernel build_join_filter_kernel(std::string const& predicate_ // Launch the JIT kernel for join filtering void launch_join_filter_kernel(jitify2::ConfiguredKernel& kernel, - cudf::table_view const& left, - cudf::table_view const& right, cudf::device_span left_indices, cudf::device_span right_indices, + std::span inputs, bool* predicate_results, std::optional user_data, - std::vector const& extra_left_cols, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) { CUDF_FUNC_RANGE(); // Create device views of tables - std::vector left_cols(left.begin(), left.end()); - left_cols.insert(left_cols.end(), extra_left_cols.begin(), extra_left_cols.end()); - std::vector right_cols(right.begin(), right.end()); + std::vector column_views; + for (auto const& input : inputs) { + if (auto* col = std::get_if(&input)) { + column_views.push_back(*col); + } else { + auto& scalar = std::get(input); + column_views.push_back(scalar.as_column_view()); + } + } - auto [left_handles, left_device_views] = - cudf::jit::column_views_to_device(left_cols, stream, mr); - auto [right_handles, right_device_views] = - cudf::jit::column_views_to_device(right_cols, stream, mr); + auto [handles, device_views] = + cudf::jit::column_views_to_device(column_views, stream, mr); // Set up kernel parameters - use JIT-compatible span type - cudf::jit::device_span left_span{left_indices.data(), left_indices.size()}; - cudf::jit::device_span right_span{right_indices.data(), - right_indices.size()}; - cudf::column_device_view_core const* left_tables_ptr = left_device_views.data(); - cudf::column_device_view_core const* right_tables_ptr = right_device_views.data(); - void* user_data_ptr = user_data.value_or(nullptr); - - std::array args{&left_span, - &right_span, - &left_tables_ptr, - &right_tables_ptr, - &predicate_results, - &user_data_ptr}; - - kernel->launch_raw(args.data()); + cudf::size_type num_rows = left_indices.size(); + cudf::size_type const* left_indices_ptr = left_indices.data(); + cudf::size_type const* right_indices_ptr = right_indices.data(); + cudf::column_device_view_core const* columns_ptr = device_views.data(); + void* user_data_ptr = user_data.value_or(nullptr); + + void* args[]{&num_rows, + &left_indices_ptr, + &right_indices_ptr, + &columns_ptr, + &predicate_results, + &user_data_ptr}; + + kernel->launch_raw(args); } // Same join semantics handling as the AST version @@ -348,43 +360,6 @@ apply_join_semantics(cudf::table_view const& left, } } -// Build template parameters from AST input specs (preserves expression input order) -jitify2::StringVec build_join_filter_template_params_from_specs( - std::vector const& input_specs, - cudf::table_view const& left, - cudf::table_view const& right, - null_aware is_null_aware) -{ - jitify2::StringVec template_params; - template_params.emplace_back(jitify2::reflection::reflect(false)); // has_user_data = false - template_params.emplace_back(jitify2::reflection::reflect(is_null_aware)); - - // Scalar columns are appended to the left table's device views, - // starting at index left.num_columns(). - auto scalar_index = left.num_columns(); - - for (auto const& spec : input_specs) { - if (std::holds_alternative(spec)) { - auto const& col_spec = std::get(spec); - auto const& table = col_spec.table == ast::table_reference::LEFT ? left : right; - auto const side_str = col_spec.table == ast::table_reference::LEFT - ? "cudf::jit::join_side::LEFT" - : "cudf::jit::join_side::RIGHT"; - auto type_name = cudf::type_to_name(table.column(col_spec.column).type()); - template_params.emplace_back( - jitify2::reflection::Template("cudf::jit::join_column_accessor") - .instantiate(type_name, std::to_string(col_spec.column), side_str)); - } else if (std::holds_alternative(spec)) { - auto const& scalar_spec = std::get(spec); - auto type_name = cudf::type_to_name(scalar_spec.ref.get().type()); - template_params.emplace_back(jitify2::reflection::Template("cudf::jit::join_scalar_accessor") - .instantiate(type_name, std::to_string(scalar_index++))); - } - } - - return template_params; -} - void validate_column_types(cudf::table_view const& table, char const* side) { for (auto const& col : table) { @@ -433,12 +408,20 @@ filter_join_indices_jit(cudf::table_view const& left, if (left_indices.empty()) { return make_empty_result(); } // Compile JIT kernel - std::vector left_cols(left.begin(), left.end()); - std::vector right_cols(right.begin(), right.end()); + std::vector inputs; + std::vector> table_sources; + for (auto const& col : left) { + inputs.emplace_back(col); + table_sources.emplace_back(0); + } + for (auto const& col : right) { + inputs.emplace_back(col); + table_sources.emplace_back(1); + } auto kernel = build_join_filter_kernel(predicate_code, - left_cols, - right_cols, + inputs, + table_sources, is_ptx, false, // has_user_data = false for now null_aware::NO, @@ -450,13 +433,11 @@ filter_join_indices_jit(cudf::table_view const& left, // Launch kernel launch_join_filter_kernel(kernel, - left, - right, left_indices, right_indices, + inputs, predicate_results.data(), std::nullopt, // no user data for now - {}, stream, mr); @@ -496,13 +477,11 @@ filter_join_indices_jit(cudf::table_view const& left, } // Convert AST predicate to JIT code - row_ir::ast_args ast_args{.left_table = left, .right_table = right}; auto filter_result = row_ir::ast_converter::filter( - row_ir::target::CUDA, predicate, ast_args, table_view{}, stream, mr); + row_ir::target::CUDA, predicate, left, right, "filter_operation", stream, mr); - // Build template params matching the AST input order - auto template_args = build_join_filter_template_params_from_specs( - filter_result.input_specs, left, right, filter_result.is_null_aware); + auto template_args = build_join_filter_template_params( + filter_result.inputs, filter_result.input_table_sources, filter_result.is_null_aware); auto const cuda_source = cudf::jit::parse_single_function_cuda(filter_result.udf, "GENERIC_JOIN_FILTER_OP"); @@ -513,23 +492,14 @@ filter_join_indices_jit(cudf::table_view const& left, cudf::jit::get_udf_kernel(*join_jit_filter_join_kernel_cu_jit, kernel_name, cuda_source); auto configured_kernel = kernel->configure_1d_max_occupancy(0, 0, nullptr, stream.value()); - // Collect scalar columns to append to left device views so join_scalar_accessor - // can read them at indices >= left.num_columns(). - std::vector scalar_cols; - for (auto const& col : filter_result.scalar_columns) { - scalar_cols.push_back(col->view()); - } - // Allocate and compute predicate results auto predicate_results = rmm::device_uvector(left_indices.size(), stream); launch_join_filter_kernel(configured_kernel, - left, - right, left_indices, right_indices, + filter_result.inputs, predicate_results.data(), std::nullopt, - scalar_cols, stream, mr); diff --git a/cpp/src/join/jit/filter_join_kernel.cu b/cpp/src/join/jit/filter_join_kernel.cu index c8b07bc7a625..61f26d261067 100644 --- a/cpp/src/join/jit/filter_join_kernel.cu +++ b/cpp/src/join/jit/filter_join_kernel.cu @@ -10,8 +10,9 @@ #include #include +#include -#include +#include #include #include @@ -31,62 +32,64 @@ namespace cudf::join::jit { // This must match the definition in cudf/join/join.hpp constexpr cudf::size_type JoinNoMatch = cuda::std::numeric_limits::min(); -template -CUDF_KERNEL void filter_join_kernel(cudf::jit::device_span left_indices, - cudf::jit::device_span right_indices, - cudf::column_device_view_core const* left_tables, - cudf::column_device_view_core const* right_tables, - bool* predicate_results, - void* user_data) +template +__device__ void execute_predicate_op(void* user_data, + size_type row_index, + cuda::std::tuple args) +{ + if constexpr (has_user_data) { + cuda::std::apply([&](auto&&... args) { GENERIC_JOIN_FILTER_OP(user_data, row_index, args...); }, + args); + } else { + cuda::std::apply([&](auto&&... args) { GENERIC_JOIN_FILTER_OP(args...); }, args); + } +} + +template +CUDF_KERNEL void filter_join_kernel(cudf::size_type num_rows, + cudf::size_type const* __restrict__ left_indices, + cudf::size_type const* __restrict__ right_indices, + cudf::column_device_view_core const* __restrict__ columns, + bool* __restrict__ predicate_results, + void* __restrict__ user_data) { auto const start = cudf::detail::grid_1d::global_thread_id(); auto const stride = cudf::detail::grid_1d::grid_stride(); - auto const size = left_indices.size(); - - for (auto i = start; i < size; i += stride) { - auto const left_idx = left_indices[i]; - auto const right_idx = right_indices[i]; + for (auto i = start; i < num_rows; i += stride) { // Skip if either index is JoinNoMatch - if (left_idx == JoinNoMatch || right_idx == JoinNoMatch) { + if (left_indices[i] == JoinNoMatch || right_indices[i] == JoinNoMatch) { predicate_results[i] = false; continue; } + cudf::size_type const* indices[] = {left_indices, right_indices}; + // Each accessor receives both tables and both indices, and internally selects // the appropriate table based on whether it's a left or right accessor. if constexpr (is_null_aware == null_aware::YES) { // Null-aware path: pass optional inputs, get optional result cuda::std::optional result{false}; - if constexpr (has_user_data) { - GENERIC_JOIN_FILTER_OP( - user_data, - i, - &result, - InputAccessors::nullable_element(left_tables, right_tables, left_idx, right_idx, i)...); - } else { - GENERIC_JOIN_FILTER_OP( - &result, - InputAccessors::nullable_element(left_tables, right_tables, left_idx, right_idx, i)...); - } + auto inputs = Accessors::map([&]() { + return cuda::std::tuple{A::nullable_element(columns, indices[A::table_index][i])...}; + }); + execute_predicate_op( + user_data, i, cuda::std::tuple_cat(cuda::std::tuple{&result}, inputs)); predicate_results[i] = result.has_value() && result.value(); } else { // Non-null-aware path: if any input is null, predicate is false - if ((InputAccessors::is_null(left_tables, right_tables, left_idx, right_idx, i) || ...)) { + auto any_null = Accessors::map( + [&]() { return (A::is_null(columns, indices[A::table_index][i]) || ...); }); + if (any_null) { predicate_results[i] = false; continue; } bool result = false; - if constexpr (has_user_data) { - GENERIC_JOIN_FILTER_OP( - user_data, - i, - &result, - InputAccessors::element(left_tables, right_tables, left_idx, right_idx, i)...); - } else { - GENERIC_JOIN_FILTER_OP( - &result, InputAccessors::element(left_tables, right_tables, left_idx, right_idx, i)...); - } + auto inputs = Accessors::map([&]() { + return cuda::std::tuple{A::element(columns, indices[A::table_index][i])...}; + }); + execute_predicate_op( + user_data, i, cuda::std::tuple_cat(cuda::std::tuple{&result}, inputs)); predicate_results[i] = result; } } diff --git a/cpp/src/join/jit/filter_join_kernel.cuh b/cpp/src/join/jit/filter_join_kernel.cuh index 5a9215e810a8..f5d086d215cc 100644 --- a/cpp/src/join/jit/filter_join_kernel.cuh +++ b/cpp/src/join/jit/filter_join_kernel.cuh @@ -17,20 +17,21 @@ namespace cudf::join::jit { * * @tparam has_user_data Whether the predicate function requires user data * @tparam is_null_aware Whether the expression needs input validity as part of its computation - * @tparam InputAccessors Variadic template for input column accessors + * @tparam Accessors type list of accessors for columns used in the predicate * @param left_indices Device span of left table indices * @param right_indices Device span of right table indices - * @param left_tables Device view of left table columns - * @param right_tables Device view of right table columns + * @param left_table Device view of left table columns + * @param right_table Device view of right table columns + * @param scalars Device view of scalar values used in the predicate * @param predicate_results Output array for predicate evaluation results * @param user_data Optional user data for predicate function */ -template -CUDF_KERNEL void filter_join_kernel(cudf::jit::device_span left_indices, - cudf::jit::device_span right_indices, - cudf::column_device_view_core const* left_tables, - cudf::column_device_view_core const* right_tables, - bool* predicate_results, - void* user_data); +template +CUDF_KERNEL void filter_join_kernel(cudf::size_type num_rows, + cudf::size_type const* __restrict__ left_indices, + cudf::size_type const* __restrict__ right_indices, + cudf::column_device_view_core const* __restrict__ columns, + bool* __restrict__ predicate_results, + void* __restrict__ user_data); } // namespace cudf::join::jit diff --git a/cpp/src/stream_compaction/filter/filter.cu b/cpp/src/stream_compaction/filter/filter.cu index 407fe0c4d3b7..9cd03b484ab0 100644 --- a/cpp/src/stream_compaction/filter/filter.cu +++ b/cpp/src/stream_compaction/filter/filter.cu @@ -22,41 +22,40 @@ namespace cudf { namespace detail { -std::vector> filter( - std::span const> predicate_inputs, - std::string const& predicate_udf, - std::vector const& filter_columns, - cudf::udf_source_type source_type, - std::optional user_data, - null_aware is_null_aware, - output_nullability predicate_nullability, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) +std::unique_ptr filter(std::string const& predicate_udf, + cudf::udf_source_type source_type, + null_aware is_null_aware, + std::optional user_data, + std::span predicate_inputs, + table_view const& filter_table, + output_nullability predicate_nullability, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { - CUDF_EXPECTS(!filter_columns.empty(), + CUDF_EXPECTS(filter_table.num_columns() > 0, "At least one column must be provided to filter.", std::invalid_argument); - auto row_size = filter_columns[0].size(); - CUDF_EXPECTS(std::all_of(filter_columns.begin(), - filter_columns.end(), + auto row_size = filter_table.num_rows(); + CUDF_EXPECTS(std::all_of(filter_table.begin(), + filter_table.end(), [&](auto const& col) { return col.size() == row_size; }), "All columns to filter must have the same number of rows.", std::invalid_argument); - auto predicate = cudf::transform_extended(predicate_inputs, - predicate_udf, - data_type{type_id::BOOL8}, - source_type, - user_data, - is_null_aware, - row_size, - predicate_nullability, - stream, - mr); - - return apply_mask( - cudf::table_view{filter_columns}, predicate->view(), mask_type::RETENTION, stream, mr) - ->release(); + transform_output outputs[] = {transform_output{data_type{type_id::BOOL8}, predicate_nullability}}; + + auto result = cudf::multi_transform(predicate_udf, + source_type, + is_null_aware, + user_data, + predicate_inputs, + outputs, + {}, + filter_table.num_rows(), + stream, + mr); + + return apply_mask(filter_table, result->get_column(0), mask_type::RETENTION, stream, mr); } } // namespace detail @@ -67,19 +66,23 @@ std::unique_ptr
filter(table_view const& predicate_table, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) { - cudf::detail::row_ir::ast_args ast_args{.table = predicate_table}; - auto args = cudf::detail::row_ir::ast_converter::filter( - cudf::detail::row_ir::target::CUDA, predicate_expr, ast_args, filter_table, stream, mr); - - return std::make_unique
(cudf::detail::filter(args.inputs, - args.udf, - args.filter_columns, - args.source_type, - args.user_data, - args.is_null_aware, - args.predicate_nullability, - stream, - mr)); + auto args = cudf::detail::row_ir::ast_converter::filter(cudf::detail::row_ir::target::CUDA, + predicate_expr, + predicate_table, + {}, + "filter_operation", + stream, + mr); + + return detail::filter(args.udf, + args.source_type, + args.is_null_aware, + args.user_data, + args.inputs, + filter_table, + args.outputs[0].nullability, + stream, + mr); } std::vector> filter_extended( @@ -94,15 +97,16 @@ std::vector> filter_extended( rmm::device_async_resource_ref mr) { CUDF_FUNC_RANGE(); - return detail::filter(predicate_inputs, - predicate_udf, - filter_columns, - source_type, - user_data, - is_null_aware, - predicate_nullability, - stream, - mr); + auto table = detail::filter(predicate_udf, + source_type, + is_null_aware, + user_data, + predicate_inputs, + table_view{filter_columns}, + predicate_nullability, + stream, + mr); + return table->release(); } std::vector> filter(std::vector const& predicate_columns, @@ -130,15 +134,16 @@ std::vector> filter(std::vector const& pred } } - return detail::filter(inputs, - predicate_udf, - filter_columns, - is_ptx ? cudf::udf_source_type::PTX : cudf::udf_source_type::CUDA, - user_data, - is_null_aware, - predicate_nullability, - stream, - mr); + auto table = detail::filter(predicate_udf, + is_ptx ? cudf::udf_source_type::PTX : cudf::udf_source_type::CUDA, + is_null_aware, + user_data, + inputs, + table_view{filter_columns}, + predicate_nullability, + stream, + mr); + return table->release(); } } // namespace cudf diff --git a/cpp/src/transform/transform.cu b/cpp/src/transform/transform.cu index dd905f59770c..b635c1c42d50 100644 --- a/cpp/src/transform/transform.cu +++ b/cpp/src/transform/transform.cu @@ -245,7 +245,7 @@ auto reflect(udf_source_type source_type, auto element = std::visit([](auto& c) { return reflect_input_element(c); }, in); bool as_scalar = std::holds_alternative(in); auto accessor = jitify2::reflection::Template("cudf::jit::column_accessor") - .instantiate(i, column, element, as_scalar); + .instantiate(i, column, element, as_scalar, 0); in_types.push_back(accessor); } @@ -257,7 +257,7 @@ auto reflect(udf_source_type source_type, auto element = std::visit([](auto& c) { return reflect_output_element(c); }, out); bool as_scalar = false; // never scalar auto accessor = jitify2::reflection::Template("cudf::jit::column_accessor") - .instantiate(i, column, element, as_scalar); + .instantiate(i, column, element, as_scalar, 0); out_types.push_back(accessor); } @@ -922,19 +922,20 @@ std::unique_ptr compute_column_jit(table_view const& table, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) { - detail::row_ir::ast_args ast_args{.table = table}; auto args = detail::row_ir::ast_converter::compute_column( - detail::row_ir::target::CUDA, expr, ast_args, stream, mr); - return transform_extended(args.inputs, - args.udf, - args.output_type, - args.source_type, - args.user_data, - args.is_null_aware, - args.row_size, - args.null_policy, - stream, - mr); + detail::row_ir::target::CUDA, expr, table, {}, "compute_operation", stream, mr); + auto result = multi_transform(args.udf, + args.source_type, + args.is_null_aware, + args.user_data, + args.inputs, + args.outputs, + std::move(args.string_offsets), + args.row_size, + stream, + mr); + auto cols = result->release(); + return std::move(cols[0]); } } // namespace cudf diff --git a/cpp/tests/jit/row_ir.cpp b/cpp/tests/jit/row_ir.cpp index 32022af56ddf..d33a14cf01a9 100644 --- a/cpp/tests/jit/row_ir.cpp +++ b/cpp/tests/jit/row_ir.cpp @@ -20,37 +20,51 @@ namespace row_ir = cudf::detail::row_ir; -struct RowIRCudaCodeGenTest : public ::testing::Test {}; +struct RowIRCudaCodeGenTest : public ::testing::Test { + std::unique_ptr f32 = + cudf::test::fixed_width_column_wrapper({1.0f, 2.0f, 3.0f}).release(); + std::unique_ptr f64 = + cudf::test::fixed_width_column_wrapper({1.0, 2.0, 3.0}).release(); + std::unique_ptr d32 = + cudf::test::fixed_point_column_wrapper({1, 2, 3}, numeric::scale_type{2}).release(); + std::unique_ptr i32 = + cudf::test::fixed_width_column_wrapper({1, 2, 3}).release(); + std::unique_ptr b8 = + cudf::test::fixed_width_column_wrapper({true, false, true}).release(); + cudf::table_view table = cudf::table_view({*f32, *f64, *d32, *i32}); +}; TEST_F(RowIRCudaCodeGenTest, GetInput) { row_ir::target_info target_info{row_ir::target::CUDA}; - row_ir::var_info inputs[] = {{"in_0", {cudf::data_type{cudf::type_id::INT32}}}, - {"in_1", {cudf::data_type{cudf::type_id::FLOAT32}}}}; - - row_ir::instance_info info{inputs, {}}; - { - row_ir::instance_context ctx{}; - row_ir::get_input get_input_0{0}; - get_input_0.instantiate(ctx, info); - auto code = get_input_0.generate_code(ctx, target_info, info); + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + [[maybe_unused]] auto in0 = ctx.add_input(*i32); + row_ir::code_sink sink; + row_ir::node get_input_0{row_ir::input_reference{0}}; + get_input_0.instantiate(ctx); + get_input_0.emit_code(ctx, target_info, sink); - auto expected_code = "int32_t tmp_0 = in_0;"; + auto expected_code = "int32_t tmp_0 = in_0;\n"; - EXPECT_EQ(code, expected_code); + EXPECT_EQ(sink.get_code(), expected_code); } { - row_ir::instance_context ctx{}; - row_ir::get_input get_input_1{1}; - get_input_1.instantiate(ctx, info); - auto null_code = get_input_1.generate_code(ctx, target_info, info); - - auto expected_null_code = "float tmp_0 = in_1;"; - - EXPECT_EQ(null_code, expected_null_code); + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + row_ir::code_sink sink; + [[maybe_unused]] auto in0 = ctx.add_input(*i32); + [[maybe_unused]] auto in1 = ctx.add_input(*f32); + row_ir::node get_input_1{row_ir::input_reference{1}}; + get_input_1.instantiate(ctx); + get_input_1.emit_code(ctx, target_info, sink); + + auto expected_null_code = "float tmp_0 = in_1;\n"; + + EXPECT_EQ(sink.get_code(), expected_null_code); } } @@ -58,39 +72,49 @@ TEST_F(RowIRCudaCodeGenTest, SetOutput) { row_ir::target_info target_info{row_ir::target::CUDA}; - row_ir::var_info inputs[] = {{"in_0", {cudf::data_type{cudf::type_id::INT32}}}, - {"in_1", {cudf::data_type{cudf::type_id::FLOAT32}}}}; - - row_ir::untyped_var_info outputs[] = {{"out_0"}, {"out_1"}}; - - row_ir::instance_info info{inputs, outputs}; - { - row_ir::instance_context ctx{}; - row_ir::set_output set_output_0{0, std::make_unique(0)}; - set_output_0.instantiate(ctx, info); - auto code = set_output_0.generate_code(ctx, target_info, info); + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + + [[maybe_unused]] auto in0 = ctx.add_input(*i32); + [[maybe_unused]] auto in1 = ctx.add_input(*f32); + [[maybe_unused]] auto out0 = ctx.add_output(); + [[maybe_unused]] auto out1 = ctx.add_output(); + row_ir::code_sink sink; + row_ir::node set_output_0{row_ir::output_reference{0}, + row_ir::node{row_ir::input_reference{0}}}; + set_output_0.instantiate(ctx); + set_output_0.emit_code(ctx, target_info, sink); auto expected_code = R"***(int32_t tmp_0 = in_0; int32_t tmp_1 = tmp_0; -*out_0 = tmp_1;)***"; +*out_0 = tmp_1; +)***"; - EXPECT_EQ(code, expected_code); + EXPECT_EQ(sink.get_code(), expected_code); } { - row_ir::instance_context ctx{}; - row_ir::set_output set_output_1{1, std::make_unique(1)}; - set_output_1.instantiate(ctx, info); - auto code = set_output_1.generate_code(ctx, target_info, info); + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + row_ir::code_sink sink; + [[maybe_unused]] auto in0 = ctx.add_input(*i32); + [[maybe_unused]] auto in1 = ctx.add_input(*f32); + [[maybe_unused]] auto out0 = ctx.add_output(); + [[maybe_unused]] auto out1 = ctx.add_output(); + row_ir::node set_output_1{row_ir::output_reference{1}, + row_ir::node{row_ir::input_reference{1}}}; + set_output_1.instantiate(ctx); + set_output_1.emit_code(ctx, target_info, sink); auto expected_code = R"***(float tmp_0 = in_1; float tmp_1 = tmp_0; -*out_1 = tmp_1;)***"; +*out_1 = tmp_1; +)***"; - EXPECT_EQ(code, expected_code); + EXPECT_EQ(sink.get_code(), expected_code); } } @@ -98,39 +122,43 @@ TEST_F(RowIRCudaCodeGenTest, UnaryOperation) { row_ir::target_info target_info{row_ir::target::CUDA}; - row_ir::var_info inputs[] = {{"in_0", {cudf::data_type{cudf::type_id::INT32}}}, - {"in_1", {cudf::data_type{cudf::type_id::DECIMAL32}}}}; - - row_ir::untyped_var_info outputs[] = {{"out_0"}, {"out_1"}}; - - row_ir::instance_info info{inputs, outputs}; - { - row_ir::instance_context ctx{}; - row_ir::operation op{row_ir::opcode::IDENTITY, - row_ir::operation::operands(row_ir::get_input(0))}; - op.instantiate(ctx, info); - auto code = op.generate_code(ctx, target_info, info); + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + [[maybe_unused]] auto in0 = ctx.add_input(*i32); + [[maybe_unused]] auto in1 = ctx.add_input(*f32); + + row_ir::code_sink sink; + row_ir::node op{ + row_ir::opcode::IDENTITY, std::nullopt, row_ir::node{row_ir::input_reference{0}}}; + op.instantiate(ctx); + op.emit_code(ctx, target_info, sink); auto expected_code = R"***(int32_t tmp_0 = in_0; -int32_t tmp_1 = cudf::ast::detail::operator_functor{}(tmp_0);)***"; +int32_t tmp_1 = cudf::ast::detail::operator_functor{}(tmp_0); +)***"; - EXPECT_EQ(code, expected_code); + EXPECT_EQ(sink.get_code(), expected_code); } { - row_ir::instance_context ctx{}; - row_ir::operation op{row_ir::opcode::IDENTITY, - row_ir::operation::operands(row_ir::get_input(1))}; - op.instantiate(ctx, info); - auto null_code = op.generate_code(ctx, target_info, info); + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + [[maybe_unused]] auto in0 = ctx.add_input(*i32); + [[maybe_unused]] auto in1 = ctx.add_input(*d32); + row_ir::code_sink sink; + row_ir::node op{ + row_ir::opcode::IDENTITY, std::nullopt, row_ir::node{row_ir::input_reference{1}}}; + op.instantiate(ctx); + op.emit_code(ctx, target_info, sink); auto expected_null_code = R"***(numeric::decimal32 tmp_0 = in_1; -numeric::decimal32 tmp_1 = cudf::ast::detail::operator_functor{}(tmp_0);)***"; +numeric::decimal32 tmp_1 = cudf::ast::detail::operator_functor{}(tmp_0); +)***"; - EXPECT_EQ(null_code, expected_null_code); + EXPECT_EQ(sink.get_code(), expected_null_code); } } @@ -138,41 +166,48 @@ TEST_F(RowIRCudaCodeGenTest, BinaryOperation) { row_ir::target_info target_info{row_ir::target::CUDA}; - row_ir::var_info inputs[] = {{"in_0", {cudf::data_type{cudf::type_id::INT32}}}, - {"in_1", {cudf::data_type{cudf::type_id::DECIMAL32}}}}; - - row_ir::untyped_var_info outputs[] = {{"out_0"}, {"out_1"}}; - - row_ir::instance_info info{inputs, outputs}; - { - row_ir::instance_context ctx{}; - row_ir::operation op{row_ir::opcode::ADD, - row_ir::operation::operands(row_ir::get_input(0), row_ir::get_input(0))}; - op.instantiate(ctx, info); - auto code = op.generate_code(ctx, target_info, info); + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + [[maybe_unused]] auto in0 = ctx.add_input(*i32); + [[maybe_unused]] auto in1 = ctx.add_input(*d32); + row_ir::code_sink sink; + row_ir::node op{row_ir::opcode::ADD, + std::nullopt, + row_ir::node{row_ir::input_reference{0}}, + row_ir::node{row_ir::input_reference{0}}}; + op.instantiate(ctx); + op.emit_code(ctx, target_info, sink); auto expected_code = R"***(int32_t tmp_0 = in_0; int32_t tmp_1 = in_0; -int32_t tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1);)***"; +int32_t tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); +)***"; - EXPECT_EQ(code, expected_code); + EXPECT_EQ(sink.get_code(), expected_code); } { - row_ir::instance_context ctx{}; - row_ir::operation op{row_ir::opcode::ADD, - row_ir::operation::operands(row_ir::get_input(1), row_ir::get_input(1))}; - op.instantiate(ctx, info); - auto null_code = op.generate_code(ctx, target_info, info); + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + [[maybe_unused]] auto in0 = ctx.add_input(*i32); + [[maybe_unused]] auto in1 = ctx.add_input(*d32); + row_ir::code_sink sink; + row_ir::node op{row_ir::opcode::ADD, + std::nullopt, + row_ir::node{row_ir::input_reference{1}}, + row_ir::node{row_ir::input_reference{1}}}; + op.instantiate(ctx); + op.emit_code(ctx, target_info, sink); auto expected_null_code = R"***(numeric::decimal32 tmp_0 = in_1; numeric::decimal32 tmp_1 = in_1; -numeric::decimal32 tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1);)***"; +numeric::decimal32 tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); +)***"; - EXPECT_EQ(null_code, expected_null_code); + EXPECT_EQ(sink.get_code(), expected_null_code); } } @@ -180,45 +215,38 @@ TEST_F(RowIRCudaCodeGenTest, VectorLengthOperation) { row_ir::target_info target_info{row_ir::target::CUDA}; - row_ir::var_info inputs[] = { - {"in_0", {cudf::data_type{cudf::type_id::FLOAT64}}}, - {"in_1", {cudf::data_type{cudf::type_id::FLOAT64}}}, - {"in_2", {cudf::data_type{cudf::type_id::FLOAT64}}}, - {"in_3", {cudf::data_type{cudf::type_id::FLOAT64}}}, - }; - - row_ir::untyped_var_info outputs[] = {{"out_0"}, {"out_1"}}; - - row_ir::instance_info info{inputs, outputs}; - auto length_operation = [&](int32_t input0, int32_t input1, int32_t output) { // This function generates the IR for the vector length operation: // length(v) = sqrt(x^2 + y^2) // where v = (x, y) and v is a 2D vector. - auto x2 = std::make_unique( - row_ir::opcode::MUL, - row_ir::operation::operands(row_ir::get_input(input0), row_ir::get_input(input0))); + auto x2 = row_ir::node(row_ir::opcode::MUL, + std::nullopt, + row_ir::node{row_ir::input_reference{input0}}, + row_ir::node{row_ir::input_reference{input0}}); - auto y2 = std::make_unique( - row_ir::opcode::MUL, - row_ir::operation::operands(row_ir::get_input(input1), row_ir::get_input(input1))); + auto y2 = row_ir::node(row_ir::opcode::MUL, + std::nullopt, + row_ir::node{row_ir::input_reference{input1}}, + row_ir::node{row_ir::input_reference{input1}}); - auto sum = std::make_unique( - row_ir::opcode::ADD, row_ir::operation::operands(std::move(x2), std::move(y2))); + auto sum = row_ir::node(row_ir::opcode::ADD, std::nullopt, std::move(x2), std::move(y2)); - auto length = std::make_unique(row_ir::opcode::SQRT, - row_ir::operation::operands(std::move(sum))); + auto length = row_ir::node(row_ir::opcode::SQRT, std::nullopt, std::move(sum)); - return std::make_unique(output, std::move(length)); + return row_ir::node(row_ir::output_reference{0}, std::move(length)); }; { - row_ir::instance_context ctx{}; + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + [[maybe_unused]] auto in0 = ctx.add_input(*f64); + [[maybe_unused]] auto in1 = ctx.add_input(*f64); + [[maybe_unused]] auto out0 = ctx.add_output(); + row_ir::code_sink sink; auto expr_ir = length_operation(0, 1, 0); - expr_ir->instantiate(ctx, info); - - auto code = expr_ir->generate_code(ctx, target_info, info); + expr_ir.instantiate(ctx); + expr_ir.emit_code(ctx, target_info, sink); auto expected_code = R"***(double tmp_0 = in_0; @@ -230,9 +258,10 @@ double tmp_5 = cudf::ast::detail::operator_functor{}(tmp_2, tmp_5); double tmp_7 = cudf::ast::detail::operator_functor{}(tmp_6); double tmp_8 = tmp_7; -*out_0 = tmp_8;)***"; +*out_0 = tmp_8; +)***"; - EXPECT_EQ(code, expected_code); + EXPECT_EQ(sink.get_code(), expected_code); } } @@ -252,12 +281,12 @@ TEST_F(RowIRCudaCodeGenTest, AstConversionBasic) auto expected = cudf::test::fixed_width_column_wrapper(expected_iter, expected_iter + column->size()); - row_ir::ast_args args{.table = cudf::table_view{{column->view()}}}; - auto transform_args = row_ir::ast_converter::compute_column(row_ir::target::CUDA, add_op, - args, + cudf::table_view{{*column}}, + cudf::table_view{}, + "expression", cudf::get_default_stream(), cudf::get_current_device_resource_ref()); @@ -265,8 +294,9 @@ TEST_F(RowIRCudaCodeGenTest, AstConversionBasic) ASSERT_EQ(transform_args.scalar_columns[0]->view().size(), 1); EXPECT_EQ(transform_args.source_type, cudf::udf_source_type::CUDA); EXPECT_EQ(transform_args.is_null_aware, cudf::null_aware::NO); - EXPECT_EQ(transform_args.null_policy, cudf::output_nullability::ALL_VALID); - EXPECT_EQ(transform_args.output_type, cudf::data_type{cudf::type_id::INT32}); + EXPECT_EQ(transform_args.outputs.size(), 1); + EXPECT_EQ(transform_args.outputs[0].nullability, cudf::output_nullability::PRESERVE); + EXPECT_EQ(transform_args.outputs[0].type, cudf::data_type{cudf::type_id::INT32}); ASSERT_EQ(transform_args.inputs.size(), 2); /// The first input should be a scalar value of 42 @@ -282,66 +312,68 @@ TEST_F(RowIRCudaCodeGenTest, AstConversionBasic) EXPECT_EQ(std::get(transform_args.inputs[1]).null_count(), column->null_count()); - auto expected_udf = R"***( -__device__ void expression(int32_t* out_0, int32_t in_0, int32_t in_1) + auto expected_udf = + R"***(__device__ void expression(int32_t* out_0, int32_t in_0, int32_t in_1) { int32_t tmp_0 = in_0; int32_t tmp_1 = in_1; int32_t tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); int32_t tmp_3 = tmp_2; *out_0 = tmp_3; - return; -} -)***"; +})***"; EXPECT_EQ(transform_args.udf, expected_udf); - auto result = cudf::transform_extended(transform_args.inputs, - transform_args.udf, - transform_args.output_type, - transform_args.source_type, - transform_args.user_data, - transform_args.is_null_aware, - transform_args.row_size, - transform_args.null_policy); + auto result = cudf::multi_transform(transform_args.udf, + transform_args.source_type, + transform_args.is_null_aware, + transform_args.user_data, + transform_args.inputs, + transform_args.outputs, + std::move(transform_args.string_offsets), + transform_args.row_size); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, result->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, result->get_column(0).view()); } TEST_F(RowIRCudaCodeGenTest, FilterPredicate) { row_ir::target_info target_info{row_ir::target::CUDA}; - row_ir::var_info inputs[] = {{"in_0", {cudf::data_type{cudf::type_id::BOOL8}}}}; - - row_ir::instance_info info{inputs, {}}; - { - row_ir::instance_context ctx{}; - row_ir::filter_predicate filter_predicate(std::make_unique(0)); - filter_predicate.instantiate(ctx, info); - auto code = filter_predicate.generate_code(ctx, target_info, info); + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + [[maybe_unused]] auto in0 = ctx.add_input(*b8); + row_ir::code_sink sink; + row_ir::node filter_predicate( + row_ir::opcode::PREDICATE, std::nullopt, row_ir::node{row_ir::input_reference{0}}); + filter_predicate.instantiate(ctx); + filter_predicate.emit_code(ctx, target_info, sink); auto expected_code = R"***(bool tmp_0 = in_0; -bool tmp_1 = cudf::ast::detail::flatten_predicate(tmp_0); +bool tmp_1 = cudf::ast::detail::predicate(tmp_0); )***"; - EXPECT_EQ(code, expected_code); + EXPECT_EQ(sink.get_code(), expected_code); } { - row_ir::instance_context ctx{}; - row_ir::filter_predicate filter_predicate(std::make_unique(0)); + row_ir::instance_context ctx{cudf::get_default_stream(), + cudf::get_current_device_resource_ref()}; + [[maybe_unused]] auto in0 = ctx.add_input(*b8); + row_ir::code_sink sink; + row_ir::node filter_predicate( + row_ir::opcode::PREDICATE, std::nullopt, row_ir::node{row_ir::input_reference{0}}); ctx.set_has_nulls(true); - filter_predicate.instantiate(ctx, info); - auto null_code = filter_predicate.generate_code(ctx, target_info, info); + filter_predicate.instantiate(ctx); + filter_predicate.emit_code(ctx, target_info, sink); auto expected_code = R"***(cuda::std::optional tmp_0 = in_0; -bool tmp_1 = cudf::ast::detail::flatten_predicate(tmp_0); +bool tmp_1 = cudf::ast::detail::predicate(tmp_0); )***"; - EXPECT_EQ(null_code, expected_code); + EXPECT_EQ(sink.get_code(), expected_code); } } From b8872f84cbe97e30b812c1089d38b6ec627337b0 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 14 May 2026 20:07:17 +0000 Subject: [PATCH 02/34] Add an operators library --- .../cudf/operators/ansi_arithmetic.cuh | 953 ++++++++++++++++++ cpp/include/cudf/operators/arithmetic.cuh | 526 ++++++++++ cpp/include/cudf/operators/bitwise.cuh | 239 +++++ cpp/include/cudf/operators/casts.cuh | 626 ++++++++++++ cpp/include/cudf/operators/comparison.cuh | 285 ++++++ cpp/include/cudf/operators/detail/promote.cuh | 66 ++ cpp/include/cudf/operators/error.hpp | 16 + cpp/include/cudf/operators/logic.cuh | 258 +++++ cpp/include/cudf/operators/math.cuh | 448 ++++++++ cpp/include/cudf/operators/null_handling.cuh | 138 +++ cpp/include/cudf/operators/trigonometric.cuh | 589 +++++++++++ cpp/include/cudf/operators/types.cuh | 92 ++ 12 files changed, 4236 insertions(+) create mode 100644 cpp/include/cudf/operators/ansi_arithmetic.cuh create mode 100644 cpp/include/cudf/operators/arithmetic.cuh create mode 100644 cpp/include/cudf/operators/bitwise.cuh create mode 100644 cpp/include/cudf/operators/casts.cuh create mode 100644 cpp/include/cudf/operators/comparison.cuh create mode 100644 cpp/include/cudf/operators/detail/promote.cuh create mode 100644 cpp/include/cudf/operators/error.hpp create mode 100644 cpp/include/cudf/operators/logic.cuh create mode 100644 cpp/include/cudf/operators/math.cuh create mode 100644 cpp/include/cudf/operators/null_handling.cuh create mode 100644 cpp/include/cudf/operators/trigonometric.cuh create mode 100644 cpp/include/cudf/operators/types.cuh diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh new file mode 100644 index 000000000000..f927d5e92005 --- /dev/null +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -0,0 +1,953 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +/** + * @brief Adds unsigned integral operands with overflow detection. + * + * @tparam T Unsigned integral type. + * @param out Destination value. + * @param a Left operand. + * @param b Right operand. + * @return errc::OVERFLOW on overflow, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) +__device__ inline errc ansi_add(T* out, T const* a, T const* b) +{ + using P = detail::promote; + auto r = static_cast

(*a) + static_cast

(*b); + if (r > static_cast

(cuda::std::numeric_limits::max())) { return errc::OVERFLOW; } + *out = static_cast(r); + return errc::OK; +} + +/** + * @brief Adds signed integral operands with overflow detection. + * + * @tparam T Signed integral type. + * @param out Destination value. + * @param a Left operand. + * @param b Right operand. + * @return errc::OVERFLOW on overflow, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_signed_v) +__device__ inline errc ansi_add(T* out, T const* a, T const* b) +{ + using P = detail::promote; + auto r = static_cast

(*a) + static_cast

(*b); + if (r > static_cast

(cuda::std::numeric_limits::max()) || + r < static_cast

(cuda::std::numeric_limits::min())) { + return errc::OVERFLOW; + } + *out = static_cast(r); + return errc::OK; +} + +/** + * @brief Adds floating-point operands. + * + * @tparam T Floating-point type. + * @param out Destination value. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template + requires(cuda::std::is_floating_point_v) +__device__ inline errc ansi_add(T* out, T const* a, T const* b) +{ + *out = *a + *b; + return errc::OK; +} + +/** + * @brief Adds fixed-point decimal operands with overflow detection. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Left operand. + * @param b Right operand. + * @return errc::OVERFLOW on overflow, else errc::OK. + */ +template +__device__ inline errc ansi_add(decimal* out, decimal const* a, decimal const* b) +{ + auto scale = cuda::std::min(a->scale(), b->scale()); + + if (numeric::addition_overflow(a->rescaled(scale).value(), b->rescaled(scale).value())) { + return errc::OVERFLOW; + } + + *out = decimal{numeric::scaled_integer{ + a->rescaled(scale).value() + b->rescaled(scale).value(), numeric::scale_type{scale}}}; + return errc::OK; +} + +/** + * @brief Adds optional operands with ANSI overflow behavior. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Left optional operand. + * @param b Right optional operand. + * @return Operation status from underlying add, or errc::OK. + */ +template +__device__ inline errc ansi_add(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + if (errc e = ansi_add(&r, &a->value(), &b->value()); e != errc::OK) { + *out = nullopt; + return e; + } + *out = r; + } else { + *out = nullopt; + } + + return errc::OK; +} + +/** + * @brief Subtracts unsigned integral operands with overflow detection. + * + * @tparam T Unsigned integral type. + * @param out Destination value. + * @param a Minuend. + * @param b Subtrahend. + * @return errc::OVERFLOW on overflow, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) +__device__ inline errc ansi_sub(T* out, T const* a, T const* b) +{ + if (*a < *b) { return errc::OVERFLOW; } + auto r = *a - *b; + *out = static_cast(r); + return errc::OK; +} + +/** + * @brief Subtracts signed integral operands with overflow detection. + * + * @tparam T Signed integral type. + * @param out Destination value. + * @param a Minuend. + * @param b Subtrahend. + * @return errc::OVERFLOW on overflow, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_signed_v) +__device__ inline errc ansi_sub(T* out, T const* a, T const* b) +{ + using P = detail::promote; + auto r = static_cast

(*a) - static_cast

(*b); + if (r > static_cast

(cuda::std::numeric_limits::max()) || + r < static_cast

(cuda::std::numeric_limits::min())) { + return errc::OVERFLOW; + } + *out = static_cast(r); + return errc::OK; +} + +/** + * @brief Subtracts floating-point operands. + * + * @tparam T Floating-point type. + * @param out Destination value. + * @param a Minuend. + * @param b Subtrahend. + * @return errc::OK. + */ +template + requires(cuda::std::is_floating_point_v) +__device__ inline errc ansi_sub(T* out, T const* a, T const* b) +{ + *out = *a - *b; + return errc::OK; +} + +/** + * @brief Subtracts fixed-point decimal operands with overflow detection. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Minuend. + * @param b Subtrahend. + * @return errc::OVERFLOW on overflow, else errc::OK. + */ +template +__device__ inline errc ansi_sub(decimal* out, decimal const* a, decimal const* b) +{ + auto scale = cuda::std::min(a->scale(), b->scale()); + + if (numeric::subtraction_overflow(a->rescaled(scale).value(), b->rescaled(scale).value())) { + return errc::OVERFLOW; + } + + *out = decimal{numeric::scaled_integer{ + a->rescaled(scale).value() - b->rescaled(scale).value(), numeric::scale_type{scale}}}; + return errc::OK; +} + +/** + * @brief Subtracts optional operands with ANSI overflow behavior. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional minuend. + * @param b Optional subtrahend. + * @return Operation status from underlying subtract, or errc::OK. + */ +template +__device__ inline errc ansi_sub(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + if (errc e = ansi_sub(&r, &a->value(), &b->value()); e != errc::OK) { + *out = nullopt; + return e; + } + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Multiplies unsigned integral operands with overflow detection. + * + * @tparam T Unsigned integral type. + * @param out Destination value. + * @param a Left operand. + * @param b Right operand. + * @return errc::OVERFLOW on overflow, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) +__device__ inline errc ansi_mul(T* out, T const* a, T const* b) +{ + using P = detail::promote; + auto r = static_cast

(*a) * static_cast

(*b); + if (r > static_cast

(cuda::std::numeric_limits::max())) { return errc::OVERFLOW; } + *out = static_cast(r); + return errc::OK; +} + +/** + * @brief Multiplies signed integral operands with overflow detection. + * + * @tparam T Signed integral type. + * @param out Destination value. + * @param a Left operand. + * @param b Right operand. + * @return errc::OVERFLOW on overflow, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_signed_v) +__device__ inline errc ansi_mul(T* out, T const* a, T const* b) +{ + using P = detail::promote; + auto r = static_cast

(*a) * static_cast

(*b); + if (r > static_cast

(cuda::std::numeric_limits::max()) || + r < static_cast

(cuda::std::numeric_limits::min())) { + return errc::OVERFLOW; + } + *out = static_cast(r); + return errc::OK; +} + +/** + * @brief Multiplies floating-point operands. + * + * @tparam T Floating-point type. + * @param out Destination value. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template + requires(cuda::std::is_floating_point_v) +__device__ inline errc ansi_mul(T* out, T const* a, T const* b) +{ + *out = *a * *b; + return errc::OK; +} + +/** + * @brief Multiplies fixed-point decimal operands with overflow detection. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Left operand. + * @param b Right operand. + * @return errc::OVERFLOW on overflow, else errc::OK. + */ +template +__device__ inline errc ansi_mul(decimal* out, decimal const* a, decimal const* b) +{ + if (numeric::multiplication_overflow(a->value(), b->value())) { return errc::OVERFLOW; } + + *out = decimal{numeric::scaled_integer{a->value() * b->value(), + numeric::scale_type{a->scale() + b->scale()}}}; + return errc::OK; +} + +/** + * @brief Multiplies optional operands with ANSI overflow behavior. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Left optional operand. + * @param b Right optional operand. + * @return Operation status from underlying multiply, or errc::OK. + */ +template +__device__ inline errc ansi_mul(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + if (errc e = ansi_mul(&r, &a->value(), &b->value()); e != errc::OK) { + *out = nullopt; + return e; + } + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Divides unsigned integral operands with divide-by-zero checks. + * + * @tparam T Unsigned integral type. + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) +__device__ inline errc ansi_div(T* out, T const* a, T const* b) +{ + if (*b == 0) { return errc::DIVISION_BY_ZERO; } + *out = static_cast(static_cast(*a) / static_cast(*b)); + return errc::OK; +} + +/** + * @brief Divides signed integral operands with ANSI overflow and divide-by-zero checks. + * + * @tparam T Signed integral type. + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::DIVISION_BY_ZERO or errc::OVERFLOW on error, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_signed_v) +__device__ inline errc ansi_div(T* out, T const* a, T const* b) +{ + if (*b == 0) { return errc::DIVISION_BY_ZERO; } + if (*a == cuda::std::numeric_limits::min() && *b == -1) { return errc::OVERFLOW; } + *out = static_cast(static_cast(*a) / static_cast(*b)); + return errc::OK; +} + +/** + * @brief Divides floating-point operands. + * + * @tparam T Floating-point type. + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +template + requires(cuda::std::is_floating_point_v) +__device__ inline errc ansi_div(T* out, T const* a, T const* b) +{ + *out = *a / *b; + return errc::OK; +} + +/** + * @brief Divides fixed-point decimal operands with ANSI checks. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OVERFLOW on overflow or zero divisor, else errc::OK. + */ +template +__device__ inline errc ansi_div(decimal* out, decimal const* a, decimal const* b) +{ + if (numeric::division_overflow(a->value(), b->value()) || b->value() == 0) { + return errc::OVERFLOW; + } + + *out = decimal{numeric::scaled_integer{a->value() / b->value(), + numeric::scale_type{a->scale() - b->scale()}}}; + return errc::OK; +} + +/** + * @brief Divides optional operands with ANSI overflow behavior. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional dividend. + * @param b Optional divisor. + * @return Operation status from underlying divide, or errc::OK. + */ +template +__device__ inline errc ansi_div(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + if (errc e = ansi_div(&r, &a->value(), &b->value()); e != errc::OK) { + *out = nullopt; + return e; + } + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes signed integral modulus with ANSI checks. + * + * @tparam T Signed integral type. + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_signed_v) +__device__ inline errc ansi_mod(T* out, T const* a, T const* b) +{ + if (*b == 0) { return errc::DIVISION_BY_ZERO; } + + // avoid signed overflow UB / trap for minimum value divided by -1. + if (*a == cuda::std::numeric_limits::min() && *b == T{-1}) { + *out = T{0}; + return errc::OK; + } + + *out = *a % *b; + return errc::OK; +} + +/** + * @brief Computes unsigned integral modulus with ANSI checks. + * + * @tparam T Unsigned integral type. + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) +__device__ inline errc ansi_mod(T* out, T const* a, T const* b) +{ + if (*b == 0) { return errc::DIVISION_BY_ZERO; } + *out = *a % *b; + return errc::OK; +} + +/** + * @brief Computes floating-point modulus for float operands with ANSI checks. + * + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. + */ +__device__ inline errc ansi_mod(float* out, float const* a, float const* b) +{ + if (*b == 0) { return errc::DIVISION_BY_ZERO; } + *out = (*a) - (*b) * ::floorf((*a) / (*b)); + return errc::OK; +} + +/** + * @brief Computes floating-point modulus for double operands with ANSI checks. + * + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. + */ +__device__ inline errc ansi_mod(double* out, double const* a, double const* b) +{ + if (*b == 0) { return errc::DIVISION_BY_ZERO; } + *out = (*a) - (*b) * ::floor((*a) / (*b)); + return errc::OK; +} + +/** + * @brief Computes fixed-point decimal modulus with ANSI checks. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Dividend. + * @param b Divisor. + * @return errc::DIVISION_BY_ZERO or propagated status, else errc::OK. + */ +template +__device__ inline errc ansi_mod(decimal* out, decimal const* a, decimal const* b) +{ + if (b->value() == 0) { return errc::DIVISION_BY_ZERO; } + + decimal div; + + if (errc e = ansi_div(&div, a, b); e != errc::OK) { return e; } + + decimal quotient; + floor("ient, &div); + *out = *a - *b * quotient; + return errc::OK; +} + +/** + * @brief Computes modulus for optional operands with ANSI behavior. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional dividend. + * @param b Optional divisor. + * @return Operation status from underlying modulus, or errc::OK. + */ +template +__device__ inline errc ansi_mod(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + if (errc e = ansi_mod(&r, &a->value(), &b->value()); e != errc::OK) { + *out = nullopt; + return e; + } + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes absolute value for signed integral inputs with ANSI overflow checks. + * + * @tparam T Signed integral type. + * @param out Destination value. + * @param a Input value. + * @return errc::OVERFLOW on minimum representable input, else errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_signed_v) +__device__ inline errc ansi_abs(T* out, T const* a) +{ + if (*a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + *out = (*a < 0) ? -(*a) : *a; + return errc::OK; +} + +/** + * @brief Returns unsigned input unchanged for ANSI absolute value. + * + * @tparam T Unsigned integral type. + * @param out Destination value. + * @param a Input value. + * @return errc::OK. + */ +template + requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) +__device__ inline errc ansi_abs(T* out, T const* a) +{ + *out = *a; + return errc::OK; +} + +/** + * @brief Computes absolute value for floating-point inputs. + * + * @tparam T Floating-point type. + * @param out Destination value. + * @param a Input value. + * @return errc::OK. + */ +template + requires(cuda::std::is_floating_point_v) +__device__ inline errc ansi_abs(T* out, T const* a) +{ + *out = (*a < 0) ? -(*a) : *a; + return errc::OK; +} + +/** + * @brief Computes absolute value for decimal inputs with ANSI overflow checks. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Input decimal value. + * @return errc::OVERFLOW on minimum representable input, else errc::OK. + */ +template +__device__ inline errc ansi_abs(decimal* out, decimal const* a) +{ + if (a->value() == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + auto rep = a->value() < 0 ? -a->value() : a->value(); + *out = decimal{numeric::scaled_integer{rep, numeric::scale_type{a->scale()}}}; + return errc::OK; +} + +template +__device__ inline errc ansi_abs(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + if (errc e = ansi_abs(&r, &a->value()); e != errc::OK) { + *out = nullopt; + return e; + } + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes unary negation for signed inputs with ANSI overflow checks. + * + * @tparam T Signed type. + * @param out Destination value. + * @param a Input value. + * @return errc::OVERFLOW on minimum representable input, else errc::OK. + */ +template + requires(cuda::std::is_signed_v) +__device__ inline errc ansi_neg(T* out, T const* a) +{ + if (*a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + *out = -(*a); + return errc::OK; +} + +/** + * @brief Computes unary negation for decimal inputs with ANSI overflow checks. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Input decimal value. + * @return errc::OVERFLOW on minimum representable input, else errc::OK. + */ +template +__device__ inline errc ansi_neg(decimal* out, decimal const* a) +{ + if (a->value() == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + auto rep = -a->value(); + *out = decimal{numeric::scaled_integer{rep, numeric::scale_type{a->scale()}}}; + return errc::OK; +} + +/** + * @brief Computes unary negation for optional inputs with ANSI checks. + * + * @tparam T Signed type. + * @param out Destination optional value. + * @param a Optional input value. + * @return Operation status from underlying negate, or errc::OK. + */ +template +__device__ inline errc ansi_neg(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + if (errc e = ansi_neg(&r, &a->value()); e != errc::OK) { + *out = nullopt; + return e; + } + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Validates decimal precision against a target precision value. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Input decimal value. + * @param precision Maximum allowed precision. + * @return errc::OVERFLOW when precision is invalid or exceeded, else errc::OK. + */ +template +__device__ inline errc ansi_precision_check(decimal* out, + decimal const* a, + int32_t const* precision) +{ + if (*precision <= 0) { return errc::OVERFLOW; } + + auto value = a->value(); + if (value == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + + auto abs_value = value < 0 ? -value : value; + + if (abs_value >= detail::ipow10(static_cast(*precision))) { return errc::OVERFLOW; } + + *out = *a; + return errc::OK; +} + +/** + * @brief Validates optional decimal precision against a precision value. + * + * @tparam T Decimal value type. + * @param out Destination optional value. + * @param a Optional decimal input. + * @param precision Precision. + * @return Operation status from underlying precision check, or errc::OK. + */ +template +__device__ inline errc ansi_precision_check(optional* out, + optional const* a, + optional const* precision) +{ + if (a->has_value() && precision->has_value()) { + T r; + if (errc e = ansi_precision_check(&r, &a->value(), &precision->value()); e != errc::OK) { + *out = nullopt; + return e; + } else { + *out = r; + return errc::OK; + } + } else { + *out = nullopt; + return errc::OK; + } +} + +/** + * @brief ANSI add that returns null instead of propagating arithmetic errors. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc ansi_try_add(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + if (errc e = ansi_add(&r, &a->value(), &b->value()); e != errc::OK) { + *out = nullopt; + } else { + *out = r; + } + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief ANSI subtract that returns null instead of propagating arithmetic errors. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional minuend. + * @param b Optional subtrahend. + * @return errc::OK. + */ +template +__device__ inline errc ansi_try_sub(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + if (errc e = ansi_sub(&r, &a->value(), &b->value()); e != errc::OK) { + *out = nullopt; + } else { + *out = r; + } + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief ANSI multiply that returns null instead of propagating arithmetic errors. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc ansi_try_mul(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + if (errc e = ansi_mul(&r, &a->value(), &b->value()); e != errc::OK) { + *out = nullopt; + } else { + *out = r; + } + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief ANSI divide that returns null instead of propagating arithmetic errors. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional dividend. + * @param b Optional divisor. + * @return errc::OK. + */ +template +__device__ inline errc ansi_try_div(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + if (errc e = ansi_div(&r, &a->value(), &b->value()); e != errc::OK) { + *out = nullopt; + } else { + *out = r; + } + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief ANSI modulus that returns null instead of propagating arithmetic errors. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional dividend. + * @param b Optional divisor. + * @return errc::OK. + */ +template +__device__ inline errc ansi_try_mod(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + if (errc e = ansi_mod(&r, &a->value(), &b->value()); e != errc::OK) { + *out = nullopt; + } else { + *out = r; + } + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief ANSI absolute value that returns null instead of propagating overflow errors. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc ansi_try_abs(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + if (errc e = ansi_abs(&r, &a->value()); e != errc::OK) { + *out = nullopt; + } else { + *out = r; + } + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief ANSI unary negation that returns null instead of propagating overflow errors. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc ansi_try_neg(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + if (errc e = ansi_neg(&r, &a->value()); e != errc::OK) { + *out = nullopt; + } else { + *out = r; + } + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief ANSI precision check that returns null instead of propagating precision errors. + * + * @tparam R Decimal representation type. + * @param out Destination optional decimal value. + * @param a Optional decimal input. + * @param precision Optional precision. + * @return errc::OK. + */ +template +__device__ inline errc ansi_try_precision_check(optional>* out, + optional> const* a, + optional const* precision) +{ + if (a->has_value() && precision->has_value()) { + decimal r; + if (errc e = ansi_precision_check(&r, &a->value(), &precision->value()); e != errc::OK) { + *out = nullopt; + } else { + *out = r; + } + } else { + *out = nullopt; + } + return errc::OK; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/arithmetic.cuh b/cpp/include/cudf/operators/arithmetic.cuh new file mode 100644 index 000000000000..31434c86c4c5 --- /dev/null +++ b/cpp/include/cudf/operators/arithmetic.cuh @@ -0,0 +1,526 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +/** + * @brief Computes absolute value for signed and floating-point inputs. + * + * @tparam T Input and output type. + * @param out Destination value. + * @param a Input value. + * @return errc::OK. + */ +template + requires(cuda::std::is_signed_v || cuda::std::is_floating_point_v) +__device__ inline errc abs(T* out, T const* a) +{ + *out = (*a < 0) ? -*a : *a; + return errc::OK; +} + +/** + * @brief Returns unsigned input unchanged for absolute value. + * + * @tparam T Unsigned input and output type. + * @param out Destination value. + * @param a Input value. + * @return errc::OK. + */ +template + requires(cuda::std::is_unsigned_v) +__device__ inline errc abs(T* out, T const* a) +{ + *out = *a; + return errc::OK; +} + +/** + * @brief Computes absolute value for fixed-point decimal values. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Input decimal value. + * @return errc::OK. + */ +template +__device__ inline errc abs(decimal* out, decimal const* a) +{ + auto rep = a->value() < 0 ? -a->value() : a->value(); + *out = decimal{numeric::scaled_integer{rep, a->scale()}}; + return errc::OK; +} + +/** + * @brief Computes absolute value for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc abs(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + abs(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes sum of two values. + * + * @tparam T Operand and result type. + * @param out Destination value. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc add(T* out, T const* a, T const* b) +{ + *out = (*a + *b); + return errc::OK; +} + +/** + * @brief Computes sum for optional operands. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc add(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + add(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes quotient of two values. + * + * @tparam T Operand and result type. + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +template +__device__ inline errc div(T* out, T const* a, T const* b) +{ + *out = (*a / *b); + return errc::OK; +} + +/** + * @brief Computes quotient for optional operands. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional dividend. + * @param b Optional divisor. + * @return errc::OK. + */ +template +__device__ inline errc div(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + div(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes floor division for integral operands. + * + * @tparam T Integral operand and result type. + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +template + requires(cuda::std::is_integral_v) +__device__ inline errc floor_div(T* out, T const* a, T const* b) +{ + *out = cudf::detail::integral_floor_div(*a, *b); + return errc::OK; +} + +/** + * @brief Computes floor division for float operands. + * + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +__device__ inline errc floor_div(float* out, float const* a, float const* b) +{ + *out = ::floorf(*a / *b); + return errc::OK; +} + +/** + * @brief Computes floor division for double operands. + * + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +__device__ inline errc floor_div(double* out, double const* a, double const* b) +{ + *out = ::floor(*a / *b); + return errc::OK; +} + +/** + * @brief Computes floor division for optional operands. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional dividend. + * @param b Optional divisor. + * @return errc::OK. + */ +template +__device__ inline errc floor_div(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + floor_div(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes remainder of integer division. + * + * @tparam T Operand and result type. + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +template +__device__ inline errc mod(T* out, T const* a, T const* b) +{ + *out = (*a % *b); + return errc::OK; +} + +/** + * @brief Computes floating-point remainder for float operands. + * + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +__device__ inline errc mod(float* out, float const* a, float const* b) +{ + *out = ::fmodf(*a, *b); + return errc::OK; +} + +/** + * @brief Computes floating-point remainder for double operands. + * + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +__device__ inline errc mod(double* out, double const* a, double const* b) +{ + *out = ::fmod(*a, *b); + return errc::OK; +} + +/** + * @brief Computes remainder for optional operands. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional dividend. + * @param b Optional divisor. + * @return errc::OK. + */ +template +__device__ inline errc mod(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + mod(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes Python-style modulus. + * + * @tparam T Integral operand and result type. + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +template +__device__ inline errc pymod(T* out, T const* a, T const* b) +{ + *out = (*a % *b + *b) % *b; + return errc::OK; +} + +/** + * @brief Computes Python-style modulus for float operands. + * + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +__device__ inline errc pymod(float* out, float const* a, float const* b) +{ + *out = ::fmodf(::fmodf(*a, *b) + *b, *b); + return errc::OK; +} + +/** + * @brief Computes Python-style modulus for double operands. + * + * @param out Destination value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +__device__ inline errc pymod(double* out, double const* a, double const* b) +{ + *out = ::fmod(::fmod(*a, *b) + *b, *b); + return errc::OK; +} + +/** + * @brief Computes Python-style modulus for optional operands. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional dividend. + * @param b Optional divisor. + * @return errc::OK. + */ +template +__device__ inline errc pymod(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + pymod(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes product of two values. + * + * @tparam T Operand and result type. + * @param out Destination value. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc mul(T* out, T const* a, T const* b) +{ + *out = (*a * *b); + return errc::OK; +} + +/** + * @brief Computes product for optional operands. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc mul(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + mul(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes unary negation for signed inputs. + * + * @tparam T Signed input and output type. + * @param out Destination value. + * @param a Input value. + * @return errc::OK. + */ +template + requires(cuda::std::is_signed_v) +__device__ inline errc neg(T* out, T const* a) +{ + *out = -(*a); + return errc::OK; +} + +/** + * @brief Computes unary negation for decimal inputs. + * + * @tparam Rep Decimal representation type. + * @param out Destination decimal value. + * @param a Input decimal value. + * @return errc::OK. + */ +template +__device__ inline errc neg(decimal* out, decimal const* a) +{ + auto rep = -a->value(); + *out = decimal{numeric::scaled_integer{rep, a->scale()}}; + return errc::OK; +} + +/** + * @brief Computes unary negation for optional input. + * + * @tparam T Signed input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc neg(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + neg(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes subtraction of two values. + * + * @tparam T Operand and result type. + * @param out Destination value. + * @param a Minuend. + * @param b Subtrahend. + * @return errc::OK. + */ +template +__device__ inline errc sub(T* out, T const* a, T const* b) +{ + *out = *a - *b; + return errc::OK; +} + +/** + * @brief Computes subtraction for optional operands. + * + * @tparam T Operand and result type. + * @param out Destination optional value. + * @param a Optional minuend. + * @param b Optional subtrahend. + * @return errc::OK. + */ +template +__device__ inline errc sub(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + sub(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes true division and returns a double. + * + * @tparam T Input operand type. + * @param out Destination double value. + * @param a Dividend. + * @param b Divisor. + * @return errc::OK. + */ +template + requires(cuda::std::is_floating_point_v || cuda::std::is_integral_v) +__device__ inline errc true_div(double* out, T const* a, T const* b) +{ + *out = static_cast(*a) / static_cast(*b); + return errc::OK; +} + +/** + * @brief Computes true division for optional operands and returns optional double. + * + * @tparam T Input operand type. + * @param out Destination optional double value. + * @param a Optional dividend. + * @param b Optional divisor. + * @return errc::OK. + */ +template +__device__ inline errc true_div(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + double r; + true_div(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/bitwise.cuh b/cpp/include/cudf/operators/bitwise.cuh new file mode 100644 index 000000000000..bd1f0919a985 --- /dev/null +++ b/cpp/include/cudf/operators/bitwise.cuh @@ -0,0 +1,239 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +/** + * @brief Computes bitwise AND of two values. + * + * @tparam T Operand and result type. + * @param out Destination for the computed value. + * @param a Left input operand. + * @param b Right input operand. + * @return errc::OK. + */ +template +__device__ inline errc bit_and(T* out, T const* a, T const* b) +{ + *out = (*a & *b); + return errc::OK; +} + +/** + * @brief Computes bitwise AND for optional operands. + * + * @tparam T Operand and result type. + * @param out Destination optional result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc bit_and(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + bit_and(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes bitwise NOT of one value. + * + * @tparam T Operand and result type. + * @param out Destination for the computed value. + * @param a Input operand. + * @return errc::OK. + */ +template +__device__ inline errc bit_invert(T* out, T const* a) +{ + *out = ~(*a); + return errc::OK; +} + +/** + * @brief Computes bitwise NOT for an optional operand. + * + * @tparam T Operand and result type. + * @param out Destination optional result. + * @param a Optional input operand. + * @return errc::OK. + */ +template +__device__ inline errc bit_invert(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + bit_invert(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes bitwise OR of two values. + * + * @tparam T Operand and result type. + * @param out Destination for the computed value. + * @param a Left input operand. + * @param b Right input operand. + * @return errc::OK. + */ +template +__device__ inline errc bit_or(T* out, T const* a, T const* b) +{ + *out = (*a | *b); + return errc::OK; +} + +/** + * @brief Computes bitwise OR for optional operands. + * + * @tparam T Operand and result type. + * @param out Destination optional result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc bit_or(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + bit_or(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes bitwise XOR of two values. + * + * @tparam T Operand and result type. + * @param out Destination for the computed value. + * @param a Left input operand. + * @param b Right input operand. + * @return errc::OK. + */ +template +__device__ inline errc bit_xor(T* out, T const* a, T const* b) +{ + *out = (*a ^ *b); + return errc::OK; +} + +/** + * @brief Computes bitwise XOR for optional operands. + * + * @tparam T Operand and result type. + * @param out Destination optional result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc bit_xor(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + bit_xor(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Shifts a value left by a bit count. + * + * @tparam T Operand and result type. + * @param out Destination for the computed value. + * @param a Input value. + * @param b Shift count. + * @return errc::OK. + */ +template +__device__ inline errc bit_shift_left(T* out, T const* a, T const* b) +{ + *out = (*a << *b); + return errc::OK; +} + +/** + * @brief Shifts an optional value left by an optional bit count. + * + * @tparam T Operand and result type. + * @param out Destination optional result. + * @param a Optional input value. + * @param b Optional shift count. + * @return errc::OK. + */ +template +__device__ inline errc bit_shift_left(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + bit_shift_left(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Shifts a value right by a bit count. + * + * @tparam T Operand and result type. + * @param out Destination for the computed value. + * @param a Input value. + * @param b Shift count. + * @return errc::OK. + */ +template +__device__ inline errc bit_shift_right(T* out, T const* a, T const* b) +{ + *out = (*a >> *b); + return errc::OK; +} + +/** + * @brief Shifts an optional value right by an optional bit count. + * + * @tparam T Operand and result type. + * @param out Destination optional result. + * @param a Optional input value. + * @param b Optional shift count. + * @return errc::OK. + */ +template +__device__ inline errc bit_shift_right(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + bit_shift_right(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/casts.cuh b/cpp/include/cudf/operators/casts.cuh new file mode 100644 index 000000000000..5d312a176ef3 --- /dev/null +++ b/cpp/include/cudf/operators/casts.cuh @@ -0,0 +1,626 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +/** + * @brief Casts input values to bool. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam T Source type. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_b8(bool* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional bool. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_b8(optional* out, optional const* a) +{ + if (a->has_value()) { + bool r; + cast_to_b8(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts input values to int8_t. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam T Source type. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_i8(int8_t* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional int8_t. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_i8(optional* out, optional const* a) +{ + if (a->has_value()) { + int8_t r; + cast_to_i8(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts input values to int16_t. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam T Source type. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_i16(int16_t* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional int16_t. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_i16(optional* out, optional const* a) +{ + if (a->has_value()) { + int16_t r; + cast_to_i16(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts input values to int32_t. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam T Source type. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_i32(int32_t* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional int32_t. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_i32(optional* out, optional const* a) +{ + if (a->has_value()) { + int32_t r; + cast_to_i32(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts input values to int64_t. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam T Source type. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_i64(int64_t* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional int64_t. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_i64(optional* out, optional const* a) +{ + if (a->has_value()) { + int64_t r; + cast_to_i64(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts input values to uint8_t. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam T Source type. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_u8(uint8_t* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional uint8_t. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_u8(optional* out, optional const* a) +{ + if (a->has_value()) { + uint8_t r; + cast_to_u8(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts input values to uint16_t. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam T Source type. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_u16(uint16_t* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional uint16_t. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_u16(optional* out, optional const* a) +{ + if (a->has_value()) { + uint16_t r; + cast_to_u16(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts input values to uint32_t. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam T Source type. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_u32(uint32_t* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional uint32_t. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_u32(optional* out, optional const* a) +{ + if (a->has_value()) { + uint32_t r; + cast_to_u32(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts input values to uint64_t. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam T Source type. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_u64(uint64_t* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional uint64_t. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_u64(optional* out, optional const* a) +{ + if (a->has_value()) { + uint64_t r; + cast_to_u64(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts input values to float. + * + * Overloads support integral, floating-point, fixed-point decimal, and optional inputs. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template + requires(std::is_integral_v || std::is_floating_point_v) +__device__ inline errc cast_to_f32(float* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts fixed-point decimal values to float. + * + * @tparam R Source decimal representation type. + * @param out Destination cast value. + * @param a Source decimal value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_f32(float* out, decimal const* a) +{ + *out = convert_fixed_to_floating(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional float. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_f32(optional* out, optional const* a) +{ + if (a->has_value()) { + float r; + cast_to_f32(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts input values to double. + * + * Overloads support integral, floating-point, fixed-point decimal, and optional inputs. + * @param out Destination cast value. + * @param a Input value. + * @return errc::OK. + */ +template + requires(std::is_integral_v || std::is_floating_point_v) +__device__ inline errc cast_to_f64(double* out, T const* a) +{ + *out = static_cast(*a); + return errc::OK; +} + +/** + * @brief Casts fixed-point decimal values to double. + * + * @tparam R Source decimal representation type. + * @param out Destination cast value. + * @param a Source decimal value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_f64(double* out, decimal const* a) +{ + *out = convert_fixed_to_floating(*a); + return errc::OK; +} + +/** + * @brief Casts optional input values to optional double. + * + * @tparam T Source type. + * @param out Destination optional cast value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_f64(optional* out, optional const* a) +{ + if (a->has_value()) { + double r; + cast_to_f64(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +namespace detail { + +/** + * @brief Casts one fixed-point decimal representation to another. + * + * @tparam To Destination representation type. + * @tparam From Source representation type. + * @param out Destination decimal value. + * @param a Source decimal value. + * @return errc::OK. + */ +template +__device__ inline errc decimal_cast(decimal* out, decimal const* a) +{ + auto rep = static_cast(a->value()); + *out = decimal{numeric::scaled_integer{rep, a->scale()}}; + return errc::OK; +} + +} // namespace detail + +// TODO: CAST_TO_DEC32 for int & float + +/** + * @brief Casts decimal input values to decimal32. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam R Source decimal representation type. + * @param out Destination decimal32 value. + * @param a Source decimal value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_dec32(numeric::decimal32* out, decimal const* a) +{ + return detail::decimal_cast(out, a); +} + +/** + * @brief Casts optional decimal input values to optional decimal32. + * + * @tparam R Source decimal representation type. + * @param out Destination optional decimal32 value. + * @param a Optional decimal input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_dec32(optional* out, + optional> const* a) +{ + if (a->has_value()) { + numeric::decimal32 r; + cast_to_dec32(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts decimal input values to decimal64. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam R Source decimal representation type. + * @param out Destination decimal64 value. + * @param a Source decimal value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_dec64(numeric::decimal64* out, decimal const* a) +{ + return detail::decimal_cast(out, a); +} + +/** + * @brief Casts optional decimal input values to optional decimal64. + * + * @tparam R Source decimal representation type. + * @param out Destination optional decimal64 value. + * @param a Optional decimal input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_dec64(optional* out, + optional> const* a) +{ + if (a->has_value()) { + numeric::decimal64 r; + cast_to_dec64(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Casts decimal input values to decimal128. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam R Source decimal representation type. + * @param out Destination decimal128 value. + * @param a Source decimal value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_dec128(numeric::decimal128* out, decimal const* a) +{ + return detail::decimal_cast(out, a); +} + +/** + * @brief Casts optional decimal input values to optional decimal128. + * + * @tparam R Source decimal representation type. + * @param out Destination optional decimal128 value. + * @param a Optional decimal input value. + * @return errc::OK. + */ +template +__device__ inline errc cast_to_dec128(optional* out, + optional> const* a) +{ + if (a->has_value()) { + numeric::decimal128 r; + cast_to_dec128(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Rescales fixed-point decimal values to a target scale. + * + * Scalar and optional overloads are provided; optional overloads propagate nulls. + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Source decimal value. + * @param new_scale Target decimal scale. + * @return errc::OK. + */ +template +__device__ inline errc rescale(decimal* out, decimal const* a, int32_t const* new_scale) +{ + *out = a->rescaled(numeric::scale_type{*new_scale}); + return errc::OK; +} + +/** + * @brief Rescales optional fixed-point decimal input values. + * + * @tparam R Decimal representation type. + * @param out Destination optional decimal value. + * @param a Optional source decimal value. + * @param new_scale Optional target decimal scale. + * @return errc::OK. + */ +template +__device__ inline errc rescale(optional>* out, + optional> const* a, + optional const* new_scale) +{ + if (a->has_value() && new_scale->has_value()) { + decimal r; + rescale(&r, &a->value(), new_scale->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/comparison.cuh b/cpp/include/cudf/operators/comparison.cuh new file mode 100644 index 000000000000..c22d96019950 --- /dev/null +++ b/cpp/include/cudf/operators/comparison.cuh @@ -0,0 +1,285 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +/** + * @brief Tests equality between two values. + * + * @tparam T Operand type. + * @param out Destination for the comparison result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc equal(bool* out, T const* a, T const* b) +{ + *out = (*a == *b); + return errc::OK; +} + +/** + * @brief Tests equality between optional operands. + * + * @tparam T Operand type. + * @param out Destination optional boolean result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc equal(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + bool r; + equal(&r, &a->value(), &b->value()); + *out = r; + } else if (!a->has_value() && !b->has_value()) { + *out = true; + } else { + *out = false; + } + return errc::OK; +} + +/** + * @brief Tests inequality between two values. + * + * @tparam T Operand type. + * @param out Destination for the comparison result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc not_equal(bool* out, T const* a, T const* b) +{ + *out = (*a != *b); + return errc::OK; +} + +/** + * @brief Tests inequality between optional operands. + * + * @tparam T Operand type. + * @param out Destination optional boolean result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc not_equal(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + bool r; + not_equal(&r, &a->value(), &b->value()); + *out = r; + } else if (!a->has_value() && !b->has_value()) { + *out = false; + } else { + *out = true; + } + return errc::OK; +} + +/** + * @brief Tests whether the left operand is greater than the right operand. + * + * @tparam T Operand type. + * @param out Destination for the comparison result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc greater(bool* out, T const* a, T const* b) +{ + *out = (*a > *b); + return errc::OK; +} + +/** + * @brief Tests whether one optional operand is greater than another. + * + * @tparam T Operand type. + * @param out Destination optional boolean result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc greater(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + bool r; + greater(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = false; + } + return errc::OK; +} + +/** + * @brief Tests whether the left operand is greater than or equal to the right operand. + * + * @tparam T Operand type. + * @param out Destination for the comparison result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc greater_equal(bool* out, T const* a, T const* b) +{ + *out = (*a >= *b); + return errc::OK; +} + +/** + * @brief Tests whether one optional operand is greater than or equal to another. + * + * @tparam T Operand type. + * @param out Destination optional boolean result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc greater_equal(optional* out, + optional const* a, + optional const* b) +{ + if (a->has_value() && b->has_value()) { + bool r; + greater_equal(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = false; + } + return errc::OK; +} + +/** + * @brief Tests whether the left operand is less than the right operand. + * + * @tparam T Operand type. + * @param out Destination for the comparison result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc less(bool* out, T const* a, T const* b) +{ + *out = (*a < *b); + return errc::OK; +} + +/** + * @brief Tests whether one optional operand is less than another. + * + * @tparam T Operand type. + * @param out Destination optional boolean result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc less(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + bool r; + less(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = false; + } + return errc::OK; +} + +/** + * @brief Tests whether the left operand is less than or equal to the right operand. + * + * @tparam T Operand type. + * @param out Destination for the comparison result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc less_equal(bool* out, T const* a, T const* b) +{ + *out = (*a <= *b); + return errc::OK; +} + +/** + * @brief Tests whether one optional operand is less than or equal to another. + * + * @tparam T Operand type. + * @param out Destination optional boolean result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc less_equal(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + bool r; + less_equal(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = false; + } + return errc::OK; +} + +/** + * @brief Tests equality between two non-optional values for null-aware equality semantics. + * + * @tparam T Operand type. + * @param out Destination for the comparison result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc null_equal(bool* out, T const* a, T const* b) +{ + *out = (*a == *b); + return errc::OK; +} + +/** + * @brief Tests null-aware equality between optional operands. + * + * @tparam T Operand type. + * @param out Destination optional boolean result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc null_equal(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + *out = (*(*a) == *(*b)); + } else if (!a->has_value() && !b->has_value()) { + *out = true; + } else { + *out = false; + } + return errc::OK; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/detail/promote.cuh b/cpp/include/cudf/operators/detail/promote.cuh new file mode 100644 index 000000000000..825a47d0926f --- /dev/null +++ b/cpp/include/cudf/operators/detail/promote.cuh @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +#include + +namespace CUDF_EXPORT cudf { +namespace ops::detail { + +/** + * @brief Type promotion map used for overflow-safe integral arithmetic. + * + * @tparam T Input integral type. + */ +template +struct promoted_t; + +template <> +struct promoted_t { + using type = int16_t; +}; + +template <> +struct promoted_t { + using type = uint16_t; +}; + +template <> +struct promoted_t { + using type = int32_t; +}; + +template <> +struct promoted_t { + using type = uint32_t; +}; + +template <> +struct promoted_t { + using type = int64_t; +}; + +template <> +struct promoted_t { + using type = uint64_t; +}; + +template <> +struct promoted_t { + using type = __int128; +}; + +template <> +struct promoted_t { + using type = unsigned __int128; +}; + +template +using promote = typename promoted_t::type; + +} // namespace ops::detail +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/error.hpp b/cpp/include/cudf/operators/error.hpp new file mode 100644 index 000000000000..59258c4461ea --- /dev/null +++ b/cpp/include/cudf/operators/error.hpp @@ -0,0 +1,16 @@ + +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +enum errc : int { OK = 0, OVERFLOW = 1, DIVISION_BY_ZERO = 2 }; + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/logic.cuh b/cpp/include/cudf/operators/logic.cuh new file mode 100644 index 000000000000..94d96ca5064f --- /dev/null +++ b/cpp/include/cudf/operators/logic.cuh @@ -0,0 +1,258 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +/** + * @brief Computes logical AND for non-optional operands with null-aware semantics. + * + * @tparam T Operand type. + * @param out Destination for the logical result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc null_logical_and(bool* out, T const* a, T const* b) +{ + *out = (*a && *b); + return errc::OK; +} + +/** + * @brief Computes logical AND for optional operands with three-valued semantics. + * + * @tparam T Operand type. + * @param out Destination optional logical result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc null_logical_and(optional* out, + optional const* a, + optional const* b) +{ + if (a->has_value() && b->has_value()) { + bool r; + null_logical_and(&r, &a->value(), &b->value()); + *out = r; + } else if (!a->has_value() && !b->has_value()) { + *out = nullopt; + } else { + if (a->has_value() ? *(*a) : *(*b)) { + *out = nullopt; + } else { + *out = false; + } + } + return errc::OK; +} + +/** + * @brief Computes logical OR for non-optional operands with null-aware semantics. + * + * @tparam T Operand type. + * @param out Destination for the logical result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc null_logical_or(bool* out, T const* a, T const* b) +{ + *out = (*a || *b); + return errc::OK; +} + +/** + * @brief Computes logical OR for optional operands with three-valued semantics. + * + * @tparam T Operand type. + * @param out Destination optional logical result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc null_logical_or(optional* out, + optional const* a, + optional const* b) +{ + if (a->has_value() && b->has_value()) { + bool r; + null_logical_or(&r, &a->value(), &b->value()); + *out = r; + } else if (!a->has_value() && !b->has_value()) { + *out = nullopt; + } else { + if (a->has_value() ? *(*a) : *(*b)) { + *out = true; + } else { + *out = nullopt; + } + } + return errc::OK; +} + +/** + * @brief Computes logical AND for non-optional operands. + * + * @tparam T Operand type. + * @param out Destination for the logical result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc logical_and(bool* out, T const* a, T const* b) +{ + *out = (*a && *b); + return errc::OK; +} + +/** + * @brief Computes logical AND for optional operands. + * + * @tparam T Operand type. + * @param out Destination optional logical result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc logical_and(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + bool r; + logical_and(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes logical OR for non-optional operands. + * + * @tparam T Operand type. + * @param out Destination for the logical result. + * @param a Left operand. + * @param b Right operand. + * @return errc::OK. + */ +template +__device__ inline errc logical_or(bool* out, T const* a, T const* b) +{ + *out = (*a || *b); + return errc::OK; +} + +/** + * @brief Computes logical OR for optional operands. + * + * @tparam T Operand type. + * @param out Destination optional logical result. + * @param a Left optional operand. + * @param b Right optional operand. + * @return errc::OK. + */ +template +__device__ inline errc logical_or(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + bool r; + logical_or(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes logical NOT for a non-optional operand. + * + * @tparam T Operand type. + * @param out Destination for the logical result. + * @param a Input operand. + * @return errc::OK. + */ +template +__device__ inline errc logical_not(bool* out, T const* a) +{ + *out = !(*a); + return errc::OK; +} + +/** + * @brief Computes logical NOT for an optional operand. + * + * @tparam T Operand type. + * @param out Destination optional logical result. + * @param a Optional input operand. + * @return errc::OK. + */ +template +__device__ inline errc logical_not(optional* out, optional const* a) +{ + if (a->has_value()) { + bool r; + logical_not(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Selects one of two values based on a boolean predicate. + * + * @tparam T Selected value type. + * @param out Destination for the selected value. + * @param true_value Value selected when @p pred is true. + * @param false_value Value selected when @p pred is false. + * @param pred Selection predicate. + * @return errc::OK. + */ +template +__device__ inline errc if_else(T* out, T const* true_value, T const* false_value, bool const* pred) +{ + *out = *pred ? *true_value : *false_value; + return errc::OK; +} + +/** + * @brief Selects one of two optional values based on an optional predicate. + * + * @tparam T Selected value type. + * @param out Destination optional selected value. + * @param true_value Optional value selected when @p pred is true. + * @param false_value Optional value selected when @p pred is false. + * @param pred Optional selection predicate. + * @return errc::OK. + */ +template +__device__ inline errc if_else(optional* out, + optional const* true_value, + optional const* false_value, + optional const* pred) +{ + if (pred->has_value() && true_value->has_value() && false_value->has_value()) { + if_else(&out->value(), &true_value->value(), &false_value->value(), &pred->value()); + } else { + *out = nullopt; + } + return errc::OK; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/math.cuh b/cpp/include/cudf/operators/math.cuh new file mode 100644 index 000000000000..f2ca028ab6f0 --- /dev/null +++ b/cpp/include/cudf/operators/math.cuh @@ -0,0 +1,448 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +/** + * @brief Computes cube root. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc cbrt(float* out, float const* a) +{ + *out = ::cbrtf(*a); + return errc::OK; +} + +/** + * @brief Computes cube root for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc cbrt(double* out, double const* a) +{ + *out = ::cbrt(*a); + return errc::OK; +} + +/** + * @brief Computes cube root for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cbrt(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + cbrt(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes ceiling. + * + * Scalar overloads support float and double, a decimal overload preserves scale, and an optional + * overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc ceil(float* out, float const* a) +{ + *out = ::ceilf(*a); + return errc::OK; +} + +/** + * @brief Computes ceiling for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc ceil(double* out, double const* a) +{ + *out = ::ceil(*a); + return errc::OK; +} + +/** + * @brief Computes ceiling for decimal input. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Input decimal value. + * @return errc::OK. + */ +template +__device__ inline errc ceil(decimal* out, decimal const* a) +{ + auto factor = detail::ipow10(static_cast(a->scale())); + auto div = a->value() / factor; + auto rem = a->value() % factor; + if (rem == 0) { + *out = *a; + } else { + auto val = a->value() > 0 ? (div + 1) : div; + *out = decimal{numeric::scaled_integer{val, a->scale()}}; + } + return errc::OK; +} + +/** + * @brief Computes ceiling for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc ceil(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + ceil(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes natural exponential. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc exp(float* out, float const* a) +{ + *out = ::expf(*a); + return errc::OK; +} + +/** + * @brief Computes natural exponential for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc exp(double* out, double const* a) +{ + *out = ::exp(*a); + return errc::OK; +} + +/** + * @brief Computes natural exponential for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc exp(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + exp(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes floor. + * + * Scalar overloads support float and double, a decimal overload preserves scale, and an optional + * overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc floor(float* out, float const* a) +{ + *out = ::floorf(*a); + return errc::OK; +} + +/** + * @brief Computes floor for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc floor(double* out, double const* a) +{ + *out = ::floor(*a); + return errc::OK; +} + +/** + * @brief Computes floor for decimal input. + * + * @tparam R Decimal representation type. + * @param out Destination decimal value. + * @param a Input decimal value. + * @return errc::OK. + */ +template +__device__ inline errc floor(decimal* out, decimal const* a) +{ + auto factor = detail::ipow10(static_cast(a->scale())); + auto div = a->value() / factor; + auto rem = a->value() % factor; + if (rem == 0) { + *out = *a; + } else { + auto val = a->value() > 0 ? div : (div - 1); + *out = decimal{numeric::scaled_integer{val, a->scale()}}; + } + return errc::OK; +} + +/** + * @brief Computes floor for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc floor(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + floor(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes natural logarithm. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc log(float* out, float const* a) +{ + *out = ::logf(*a); + return errc::OK; +} + +/** + * @brief Computes natural logarithm for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc log(double* out, double const* a) +{ + *out = ::log(*a); + return errc::OK; +} + +/** + * @brief Computes natural logarithm for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc log(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + log(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes exponentiation. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Base value. + * @param b Exponent value. + * @return errc::OK. + */ +__device__ inline errc pow(float* out, float const* a, float const* b) +{ + *out = ::powf(*a, *b); + return errc::OK; +} + +/** + * @brief Computes exponentiation for double input. + * + * @param out Destination for the computed value. + * @param a Base value. + * @param b Exponent value. + * @return errc::OK. + */ +__device__ inline errc pow(double* out, double const* a, double const* b) +{ + *out = ::pow(*a, *b); + return errc::OK; +} + +/** + * @brief Computes exponentiation for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional base value. + * @param b Optional exponent value. + * @return errc::OK. + */ +template +__device__ inline errc pow(optional* out, optional const* a, optional const* b) +{ + if (a->has_value() && b->has_value()) { + T r; + pow(&r, &a->value(), &b->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Rounds to integral value using current rounding mode. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc rint(float* out, float const* a) +{ + *out = ::rintf(*a); + return errc::OK; +} + +/** + * @brief Rounds to integral value for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc rint(double* out, double const* a) +{ + *out = ::rint(*a); + return errc::OK; +} + +/** + * @brief Rounds to integral value for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc rint(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + rint(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes square root. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc sqrt(float* out, float const* a) +{ + *out = ::sqrtf(*a); + return errc::OK; +} + +/** + * @brief Computes square root for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc sqrt(double* out, double const* a) +{ + *out = ::sqrt(*a); + return errc::OK; +} + +/** + * @brief Computes square root for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc sqrt(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + sqrt(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/null_handling.cuh b/cpp/include/cudf/operators/null_handling.cuh new file mode 100644 index 000000000000..3c21f2f98960 --- /dev/null +++ b/cpp/include/cudf/operators/null_handling.cuh @@ -0,0 +1,138 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +/** + * @brief Returns false for non-optional inputs. + * + * @tparam T Input type. + * @param out Destination for the null test result. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc is_null(bool* out, T const* a) +{ + *out = false; + return errc::OK; +} + +/** + * @brief Tests whether an optional input is null. + * + * @tparam T Input value type. + * @param out Destination optional boolean result. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc is_null(optional* out, optional const* a) +{ + *out = !a->has_value(); + return errc::OK; +} + +/** + * @brief Sets the output to null when the condition is true. + * + * @tparam T Value type. + * @param out Destination optional value. + * @param a Optional input value. + * @param condition Optional boolean condition. + * @return errc::OK. + */ +template +__device__ inline errc nullify_if(optional* out, + optional const* a, + optional const* condition) +{ + if (condition->has_value() && a->has_value()) { + if (condition->value()) { + *out = nullopt; + } else { + *out = a->value(); + } + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Returns the first non-null input value of two non-nullable values. + * + * @tparam T Value type. + * @param out Destination value. + * @param a First value. + * @param b Second value. + * @return errc::OK. + */ +template +__device__ inline errc coalesce(T* out, T const* a, T const* b) +{ + *out = *a; + return errc::OK; +} + +/** + * @brief Returns the first non-null optional value of two optional values, otherwise null. + * + * @tparam T Value type. + * @param out Destination optional value. + * @param a First optional value. + * @param b Second optional value. + * @return errc::OK. + */ +template +__device__ inline errc coalesce(optional* out, optional const* a, optional const* b) +{ + if (a->has_value()) { + *out = a->value(); + } else if (b->has_value()) { + *out = b->value(); + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Returns the input boolean predicate unchanged. + * + * @param out Destination boolean predicate. + * @param a Input boolean predicate. + * @return errc::OK. + */ +__device__ inline errc predicate(bool* out, bool const* a) +{ + *out = *a; + return errc::OK; +} + +/** + * @brief Converts an optional predicate to a non-nullable predicate. + * + * @param out Destination optional boolean predicate. + * @param a Optional input boolean predicate. + * @return errc::OK. + */ +__device__ inline errc predicate(optional* out, optional const* a) +{ + if (a->has_value()) { + *out = a->value(); + } else { + *out = false; + } + return errc::OK; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/trigonometric.cuh b/cpp/include/cudf/operators/trigonometric.cuh new file mode 100644 index 000000000000..42f254d28b4e --- /dev/null +++ b/cpp/include/cudf/operators/trigonometric.cuh @@ -0,0 +1,589 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +/** + * @brief Computes inverse cosine. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arccos(float* out, float const* a) +{ + *out = ::acosf(*a); + return errc::OK; +} + +/** + * @brief Computes inverse cosine for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arccos(double* out, double const* a) +{ + *out = ::acos(*a); + return errc::OK; +} + +/** + * @brief Computes inverse cosine for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc arccos(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + arccos(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes inverse hyperbolic cosine. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arccosh(float* out, float const* a) +{ + *out = ::acoshf(*a); + return errc::OK; +} + +/** + * @brief Computes inverse hyperbolic cosine for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arccosh(double* out, double const* a) +{ + *out = ::acosh(*a); + return errc::OK; +} + +/** + * @brief Computes inverse hyperbolic cosine for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc arccosh(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + arccosh(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes inverse sine. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arcsin(float* out, float const* a) +{ + *out = ::asinf(*a); + return errc::OK; +} + +/** + * @brief Computes inverse sine for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arcsin(double* out, double const* a) +{ + *out = ::asin(*a); + return errc::OK; +} + +/** + * @brief Computes inverse sine for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc arcsin(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + arcsin(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes inverse hyperbolic sine. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arcsinh(float* out, float const* a) +{ + *out = ::asinhf(*a); + return errc::OK; +} + +/** + * @brief Computes inverse hyperbolic sine for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arcsinh(double* out, double const* a) +{ + *out = ::asinh(*a); + return errc::OK; +} + +/** + * @brief Computes inverse hyperbolic sine for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc arcsinh(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + arcsinh(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes inverse tangent. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arctan(float* out, float const* a) +{ + *out = ::atanf(*a); + return errc::OK; +} + +/** + * @brief Computes inverse tangent for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arctan(double* out, double const* a) +{ + *out = ::atan(*a); + return errc::OK; +} + +/** + * @brief Computes inverse tangent for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc arctan(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + arctan(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes inverse hyperbolic tangent. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arctanh(float* out, float const* a) +{ + *out = ::atanhf(*a); + return errc::OK; +} + +/** + * @brief Computes inverse hyperbolic tangent for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc arctanh(double* out, double const* a) +{ + *out = ::atanh(*a); + return errc::OK; +} + +/** + * @brief Computes inverse hyperbolic tangent for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc arctanh(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + arctanh(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes cosine. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc cos(float* out, float const* a) +{ + *out = ::cosf(*a); + return errc::OK; +} + +/** + * @brief Computes cosine for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc cos(double* out, double const* a) +{ + *out = ::cos(*a); + return errc::OK; +} + +/** + * @brief Computes cosine for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cos(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + cos(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes hyperbolic cosine. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc cosh(float* out, float const* a) +{ + *out = ::coshf(*a); + return errc::OK; +} + +/** + * @brief Computes hyperbolic cosine for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc cosh(double* out, double const* a) +{ + *out = ::cosh(*a); + return errc::OK; +} + +/** + * @brief Computes hyperbolic cosine for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc cosh(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + cosh(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes sine. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc sin(float* out, float const* a) +{ + *out = ::sinf(*a); + return errc::OK; +} + +/** + * @brief Computes sine for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc sin(double* out, double const* a) +{ + *out = ::sin(*a); + return errc::OK; +} + +/** + * @brief Computes sine for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc sin(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + sin(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes hyperbolic sine. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc sinh(float* out, float const* a) +{ + *out = ::sinhf(*a); + return errc::OK; +} + +/** + * @brief Computes hyperbolic sine for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc sinh(double* out, double const* a) +{ + *out = ::sinh(*a); + return errc::OK; +} + +/** + * @brief Computes hyperbolic sine for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc sinh(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + sinh(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes tangent. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc tan(float* out, float const* a) +{ + *out = ::tanf(*a); + return errc::OK; +} + +/** + * @brief Computes tangent for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc tan(double* out, double const* a) +{ + *out = ::tan(*a); + return errc::OK; +} + +/** + * @brief Computes tangent for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc tan(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + tan(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +/** + * @brief Computes hyperbolic tangent. + * + * Scalar overloads support float and double inputs, and an optional overload propagates nulls. + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc tanh(float* out, float const* a) +{ + *out = ::tanhf(*a); + return errc::OK; +} + +/** + * @brief Computes hyperbolic tangent for double input. + * + * @param out Destination for the computed value. + * @param a Input value. + * @return errc::OK. + */ +__device__ inline errc tanh(double* out, double const* a) +{ + *out = ::tanh(*a); + return errc::OK; +} + +/** + * @brief Computes hyperbolic tangent for optional input. + * + * @tparam T Input and output type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc tanh(optional* out, optional const* a) +{ + if (a->has_value()) { + T r; + tanh(&r, &a->value()); + *out = r; + } else { + *out = nullopt; + } + return errc::OK; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/types.cuh b/cpp/include/cudf/operators/types.cuh new file mode 100644 index 000000000000..447b247d5f3e --- /dev/null +++ b/cpp/include/cudf/operators/types.cuh @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +template +using optional = cuda::std::optional; + +inline constexpr auto nullopt = cuda::std::nullopt; + +template +using decimal = numeric::fixed_point; + +template +using duration = cuda::std::chrono::duration; + +namespace detail { + +/** + * @brief Computes an integer power of ten. + * + * @tparam T Integral exponent and return type. + * @param exponent Exponent value. + * @return Ten raised to @p exponent. + */ +template +__device__ constexpr T ipow10(T exponent) +{ + if (exponent == 0) { return 1; } + + T extra = 1; + T square = 10; + T n = exponent; + + while (n > 1) { + if ((n & 1) == 1) { extra *= square; } + n >>= 1; + square *= square; + } + + return square * extra; +} + +} // namespace detail + +/** + * @brief Copies an input value to the output. + * + * @tparam T Value type. + * @param out Destination value. + * @param a Input value. + * @return errc::OK. + */ +template +__device__ inline errc identity(T* out, T const* a) +{ + *out = *a; + return errc::OK; +} + +/** + * @brief Copies an optional input value to the output. + * + * @tparam T Value type. + * @param out Destination optional value. + * @param a Optional input value. + * @return errc::OK. + */ +template +__device__ inline errc identity(optional* out, optional const* a) +{ + *out = *a; + return errc::OK; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf From cbaaa96296d6fc094d32617493f72a15062cc4f3 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 14 May 2026 20:44:09 +0000 Subject: [PATCH 03/34] documentation, test, & refactoring fixes --- cpp/src/jit/row_ir.cpp | 17 +++++------------ cpp/src/join/jit/filter_join_kernel.cuh | 4 +--- cpp/tests/filter/filter_test.cpp | 9 +++++---- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/cpp/src/jit/row_ir.cpp b/cpp/src/jit/row_ir.cpp index acf17772c00c..54c90d5de025 100644 --- a/cpp/src/jit/row_ir.cpp +++ b/cpp/src/jit/row_ir.cpp @@ -303,6 +303,9 @@ void node::instantiate(instance_context& ctx) type_ = args_[0]->get_type(); } break; case opcode::PREDICATE: { + CUDF_EXPECTS(args_[0]->get_type().id() == type_id::BOOL8, + "Predicate operator requires a boolean argument.", + std::runtime_error); type_ = data_type{type_id::BOOL8, 0}; } break; default: { @@ -451,7 +454,7 @@ std::tuple ast_converter::generate_ bool output_is_always_valid = std::all_of( output_irs_.cbegin(), output_irs_.cend(), [](auto& ir) { return ir->is_always_valid(); }); - bool may_evaluate_null = !output_is_always_valid || has_nullable_inputs; + bool may_evaluate_null = output_is_always_valid ? false : (has_nullable_inputs || is_null_aware); auto null_policy = may_evaluate_null ? output_nullability::PRESERVE : output_nullability::ALL_VALID; @@ -595,17 +598,7 @@ transform_args ast_converter::filter(target target_id, rmm::device_async_resource_ref mr) { auto filter = ast::detail::predicate{expr}; - auto transform = - compute_column(target_id, filter, left_table, right_table, function_name, stream, mr); - - CUDF_EXPECTS(transform.outputs.size() == 1, - "Filter expression must have exactly one output column.", - std::invalid_argument); - CUDF_EXPECTS(transform.outputs[0].type.id() == type_id::BOOL8, - "Filter expression must return a boolean type.", - std::invalid_argument); - - return transform; + return compute_column(target_id, filter, left_table, right_table, function_name, stream, mr); } } // namespace cudf::detail::row_ir diff --git a/cpp/src/join/jit/filter_join_kernel.cuh b/cpp/src/join/jit/filter_join_kernel.cuh index f5d086d215cc..cd988de501d4 100644 --- a/cpp/src/join/jit/filter_join_kernel.cuh +++ b/cpp/src/join/jit/filter_join_kernel.cuh @@ -20,9 +20,7 @@ namespace cudf::join::jit { * @tparam Accessors type list of accessors for columns used in the predicate * @param left_indices Device span of left table indices * @param right_indices Device span of right table indices - * @param left_table Device view of left table columns - * @param right_table Device view of right table columns - * @param scalars Device view of scalar values used in the predicate + * @param columns Device view of all columns involved in the predicate * @param predicate_results Output array for predicate evaluation results * @param user_data Optional user data for predicate function */ diff --git a/cpp/tests/filter/filter_test.cpp b/cpp/tests/filter/filter_test.cpp index 78c8b536bef8..01ad59d9e258 100644 --- a/cpp/tests/filter/filter_test.cpp +++ b/cpp/tests/filter/filter_test.cpp @@ -161,13 +161,14 @@ TEST_F(FilterTest, ScalarFilter) std::string cuda = R"***( __device__ void is_divisible(bool* out, int32_t a, int32_t b) { *out = ((a % b) == 0); } )***"; + auto expected = cudf::test::fixed_width_column_wrapper{{2, 4, 6, 8, 10}}; cudf::filter_input inputs[] = {a, cudf::scalar_column_view(b)}; - EXPECT_THROW( - cudf::filter_extended( - inputs, cuda, {a, b}, cudf::udf_source_type::CUDA, std::nullopt, cudf::null_aware::NO), - std::invalid_argument); + auto result = cudf::filter_extended( + inputs, cuda, {a}, cudf::udf_source_type::CUDA, std::nullopt, cudf::null_aware::NO); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, result[0]->view()); } TEST_F(FilterTest, MixedTypes) From 0f0c1342019f66bf42e3934f67aa1ed5b4453eca Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 14 May 2026 20:49:15 +0000 Subject: [PATCH 04/34] documentation, test, & refactoring fixes --- cpp/tests/jit/row_ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/jit/row_ir.cpp b/cpp/tests/jit/row_ir.cpp index d33a14cf01a9..b1e5d1cd0a3b 100644 --- a/cpp/tests/jit/row_ir.cpp +++ b/cpp/tests/jit/row_ir.cpp @@ -295,7 +295,7 @@ TEST_F(RowIRCudaCodeGenTest, AstConversionBasic) EXPECT_EQ(transform_args.source_type, cudf::udf_source_type::CUDA); EXPECT_EQ(transform_args.is_null_aware, cudf::null_aware::NO); EXPECT_EQ(transform_args.outputs.size(), 1); - EXPECT_EQ(transform_args.outputs[0].nullability, cudf::output_nullability::PRESERVE); + EXPECT_EQ(transform_args.outputs[0].nullability, cudf::output_nullability::ALL_VALID); EXPECT_EQ(transform_args.outputs[0].type, cudf::data_type{cudf::type_id::INT32}); ASSERT_EQ(transform_args.inputs.size(), 2); From c612ac824b2e83a4b9d81cf73b594ffeb308acf7 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 14 May 2026 21:04:23 +0000 Subject: [PATCH 05/34] Add validation for predicate input column sizes in filter function --- cpp/src/stream_compaction/filter/filter.cu | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cpp/src/stream_compaction/filter/filter.cu b/cpp/src/stream_compaction/filter/filter.cu index 9cd03b484ab0..c2c9fc73c6c8 100644 --- a/cpp/src/stream_compaction/filter/filter.cu +++ b/cpp/src/stream_compaction/filter/filter.cu @@ -41,6 +41,16 @@ std::unique_ptr

filter(std::string const& predicate_udf, [&](auto const& col) { return col.size() == row_size; }), "All columns to filter must have the same number of rows.", std::invalid_argument); + CUDF_EXPECTS(std::all_of(predicate_inputs.begin(), + predicate_inputs.end(), + [&](auto& input) { + if (auto* col = std::get_if(&input)) { + return col->size() == row_size; + } + return true; + }), + "All predicate input columns must have the same number of rows as the filter table.", + std::invalid_argument); transform_output outputs[] = {transform_output{data_type{type_id::BOOL8}, predicate_nullability}}; From 01320e6441afd77341e2b7c0f54dda29feb71ebd Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 14 May 2026 22:47:14 +0000 Subject: [PATCH 06/34] remove linter warning --- cpp/src/jit/row_ir.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/src/jit/row_ir.cpp b/cpp/src/jit/row_ir.cpp index 54c90d5de025..0d3b58dc89e1 100644 --- a/cpp/src/jit/row_ir.cpp +++ b/cpp/src/jit/row_ir.cpp @@ -194,7 +194,10 @@ node::node(opcode op, std::optional target_scale, std::vector arg) : reference_{reference}, op_{opcode::SET_OUTPUT} From a4f14ef0b285a386b3298146dd3b36d614c4ddda Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Fri, 15 May 2026 01:47:08 +0000 Subject: [PATCH 07/34] doc fix --- cpp/src/join/jit/filter_join_kernel.cuh | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/src/join/jit/filter_join_kernel.cuh b/cpp/src/join/jit/filter_join_kernel.cuh index cd988de501d4..d6dc585c5ea8 100644 --- a/cpp/src/join/jit/filter_join_kernel.cuh +++ b/cpp/src/join/jit/filter_join_kernel.cuh @@ -18,6 +18,7 @@ namespace cudf::join::jit { * @tparam has_user_data Whether the predicate function requires user data * @tparam is_null_aware Whether the expression needs input validity as part of its computation * @tparam Accessors type list of accessors for columns used in the predicate + * @param num_rows Number of rows to process * @param left_indices Device span of left table indices * @param right_indices Device span of right table indices * @param columns Device view of all columns involved in the predicate From ac99883c8f8d9488b068596f9bbb6b26eb36d31a Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Tue, 19 May 2026 10:23:01 +0100 Subject: [PATCH 08/34] Update cpp/src/join/filter_join_indices_jit.cu Co-authored-by: Bradley Dice --- cpp/src/join/filter_join_indices_jit.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/join/filter_join_indices_jit.cu b/cpp/src/join/filter_join_indices_jit.cu index f1d1d3c2b9cf..70cfa77ddf78 100644 --- a/cpp/src/join/filter_join_indices_jit.cu +++ b/cpp/src/join/filter_join_indices_jit.cu @@ -76,7 +76,7 @@ jitify2::StringVec build_join_filter_template_params( "cudf::column_device_view_core", element, true, - 0 // scalars dont belong to a table, so just use 0 as placeholder + 0 // scalars don't belong to a table, so just use 0 as placeholder )); } } From 6d92a299d1cb0381381af79b1b9f7ae4c3a676f7 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Wed, 20 May 2026 11:36:00 +0000 Subject: [PATCH 09/34] fix user_data usage --- cpp/src/join/filter_join_indices_jit.cu | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cpp/src/join/filter_join_indices_jit.cu b/cpp/src/join/filter_join_indices_jit.cu index 70cfa77ddf78..15b532cac030 100644 --- a/cpp/src/join/filter_join_indices_jit.cu +++ b/cpp/src/join/filter_join_indices_jit.cu @@ -51,10 +51,11 @@ namespace { jitify2::StringVec build_join_filter_template_params( std::span inputs, std::span> table_sources, + bool has_user_data, null_aware is_null_aware) { jitify2::StringVec template_params; - template_params.emplace_back(jitify2::reflection::reflect(false)); // has_user_data = false + template_params.emplace_back(jitify2::reflection::reflect(has_user_data)); template_params.emplace_back(jitify2::reflection::reflect(is_null_aware)); jitify2::StringVec accessors; @@ -120,7 +121,8 @@ jitify2::ConfiguredKernel build_join_filter_kernel(std::string const& predicate_ : cudf::jit::parse_single_function_cuda(predicate_code, "GENERIC_JOIN_FILTER_OP"); // Build template parameters and kernel name - auto template_args = build_join_filter_template_params(inputs, table_sources, is_null_aware); + auto template_args = + build_join_filter_template_params(inputs, table_sources, has_user_data, is_null_aware); auto kernel_name = jitify2::reflection::Template("cudf::join::jit::filter_join_kernel").instantiate(template_args); @@ -480,8 +482,10 @@ filter_join_indices_jit(cudf::table_view const& left, auto filter_result = row_ir::ast_converter::filter( row_ir::target::CUDA, predicate, left, right, "filter_operation", stream, mr); - auto template_args = build_join_filter_template_params( - filter_result.inputs, filter_result.input_table_sources, filter_result.is_null_aware); + auto template_args = build_join_filter_template_params(filter_result.inputs, + filter_result.input_table_sources, + filter_result.user_data.has_value(), + filter_result.is_null_aware); auto const cuda_source = cudf::jit::parse_single_function_cuda(filter_result.udf, "GENERIC_JOIN_FILTER_OP"); @@ -499,7 +503,7 @@ filter_join_indices_jit(cudf::table_view const& left, right_indices, filter_result.inputs, predicate_results.data(), - std::nullopt, + filter_result.user_data, stream, mr); From 77993a0664ac35cbc2f7f057fd7dcce062f54311 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Wed, 20 May 2026 13:48:47 +0000 Subject: [PATCH 10/34] bug fixes --- cpp/include/cudf/operators/ansi_arithmetic.cuh | 8 ++++++++ cpp/include/cudf/operators/casts.cuh | 2 +- cpp/include/cudf/operators/logic.cuh | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index f927d5e92005..668d24dffdbc 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -619,6 +619,14 @@ __device__ inline errc ansi_abs(decimal* out, decimal const* a) return errc::OK; } +/** + * `@brief` Computes absolute value for optional inputs with ANSI overflow checks. + * + * `@tparam` T Value type. + * `@param` out Destination optional value. + * `@param` a Optional input value. + * `@return` Operation status from underlying abs, or errc::OK. + */ template __device__ inline errc ansi_abs(optional* out, optional const* a) { diff --git a/cpp/include/cudf/operators/casts.cuh b/cpp/include/cudf/operators/casts.cuh index 5d312a176ef3..f18da8ae076a 100644 --- a/cpp/include/cudf/operators/casts.cuh +++ b/cpp/include/cudf/operators/casts.cuh @@ -614,7 +614,7 @@ __device__ inline errc rescale(optional>* out, { if (a->has_value() && new_scale->has_value()) { decimal r; - rescale(&r, &a->value(), new_scale->value()); + rescale(&r, &a->value(), &new_scale->value()); *out = r; } else { *out = nullopt; diff --git a/cpp/include/cudf/operators/logic.cuh b/cpp/include/cudf/operators/logic.cuh index 94d96ca5064f..8d4873380f48 100644 --- a/cpp/include/cudf/operators/logic.cuh +++ b/cpp/include/cudf/operators/logic.cuh @@ -247,7 +247,9 @@ __device__ inline errc if_else(optional* out, optional const* pred) { if (pred->has_value() && true_value->has_value() && false_value->has_value()) { - if_else(&out->value(), &true_value->value(), &false_value->value(), &pred->value()); + T r; + if_else(&r, &true_value->value(), &false_value->value(), &pred->value()); + *out = r; } else { *out = nullopt; } From 655689a8664de999e2ce7e1e70d3cf0a8a254923 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Wed, 20 May 2026 13:53:30 +0000 Subject: [PATCH 11/34] docs: clarify parameter description for ipow10 function --- cpp/include/cudf/operators/types.cuh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/include/cudf/operators/types.cuh b/cpp/include/cudf/operators/types.cuh index 447b247d5f3e..f71611170042 100644 --- a/cpp/include/cudf/operators/types.cuh +++ b/cpp/include/cudf/operators/types.cuh @@ -35,8 +35,9 @@ namespace detail { * @brief Computes an integer power of ten. * * @tparam T Integral exponent and return type. - * @param exponent Exponent value. + * @param exponent Non-negative exponent value. * @return Ten raised to @p exponent. + * @pre exponent >= 0. */ template __device__ constexpr T ipow10(T exponent) From 4775b56552cff184d069d0cd01d7e30802986fe3 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Wed, 20 May 2026 14:10:14 +0000 Subject: [PATCH 12/34] docs: fix formatting of comments for ansi_abs function --- cpp/include/cudf/operators/ansi_arithmetic.cuh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index 668d24dffdbc..ab7dc1fe5ae2 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -620,12 +620,12 @@ __device__ inline errc ansi_abs(decimal* out, decimal const* a) } /** - * `@brief` Computes absolute value for optional inputs with ANSI overflow checks. + * @brief Computes absolute value for optional inputs with ANSI overflow checks. * - * `@tparam` T Value type. - * `@param` out Destination optional value. - * `@param` a Optional input value. - * `@return` Operation status from underlying abs, or errc::OK. + * @tparam T Value type. + * @param out Destination optional value. + * @param a Optional input value. + * @return Operation status from underlying abs, or errc::OK. */ template __device__ inline errc ansi_abs(optional* out, optional const* a) From 29bec09a566f556247d65237d807ad2e1901204a Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Wed, 20 May 2026 14:47:59 +0000 Subject: [PATCH 13/34] fix: handle negative scale in ceil and floor functions for decimal types --- cpp/include/cudf/operators/math.cuh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/cpp/include/cudf/operators/math.cuh b/cpp/include/cudf/operators/math.cuh index f2ca028ab6f0..e6abd4187e4d 100644 --- a/cpp/include/cudf/operators/math.cuh +++ b/cpp/include/cudf/operators/math.cuh @@ -96,14 +96,18 @@ __device__ inline errc ceil(double* out, double const* a) template __device__ inline errc ceil(decimal* out, decimal const* a) { - auto factor = detail::ipow10(static_cast(a->scale())); + if (a->scale() >= 0) { + *out = *a; + return errc::OK; + } + auto factor = detail::ipow10(-static_cast(a->scale())); auto div = a->value() / factor; auto rem = a->value() % factor; if (rem == 0) { *out = *a; } else { auto val = a->value() > 0 ? (div + 1) : div; - *out = decimal{numeric::scaled_integer{val, a->scale()}}; + *out = decimal{numeric::scaled_integer{val * factor, a->scale()}}; } return errc::OK; } @@ -216,14 +220,18 @@ __device__ inline errc floor(double* out, double const* a) template __device__ inline errc floor(decimal* out, decimal const* a) { - auto factor = detail::ipow10(static_cast(a->scale())); + if (a->scale() >= 0) { + *out = *a; + return errc::OK; + } + auto factor = detail::ipow10(-static_cast(a->scale())); auto div = a->value() / factor; auto rem = a->value() % factor; if (rem == 0) { *out = *a; } else { auto val = a->value() > 0 ? div : (div - 1); - *out = decimal{numeric::scaled_integer{val, a->scale()}}; + *out = decimal{numeric::scaled_integer{val * factor, a->scale()}}; } return errc::OK; } From c7c87450c207f8dfd6a0c0674c01f543547654cd Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Wed, 20 May 2026 20:40:09 +0000 Subject: [PATCH 14/34] refactor: simplify arithmetic operations with overflow detection for integral types --- .../cudf/operators/ansi_arithmetic.cuh | 141 ++++-------------- 1 file changed, 26 insertions(+), 115 deletions(-) diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index ab7dc1fe5ae2..14d05ba4a96c 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -8,49 +8,27 @@ #include #include +#include + namespace CUDF_EXPORT cudf { namespace ops { /** - * @brief Adds unsigned integral operands with overflow detection. - * - * @tparam T Unsigned integral type. - * @param out Destination value. - * @param a Left operand. - * @param b Right operand. - * @return errc::OVERFLOW on overflow, else errc::OK. - */ -template - requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) -__device__ inline errc ansi_add(T* out, T const* a, T const* b) -{ - using P = detail::promote; - auto r = static_cast

(*a) + static_cast

(*b); - if (r > static_cast

(cuda::std::numeric_limits::max())) { return errc::OVERFLOW; } - *out = static_cast(r); - return errc::OK; -} - -/** - * @brief Adds signed integral operands with overflow detection. + * @brief Adds integral operands with overflow detection. * - * @tparam T Signed integral type. + * @tparam T Integral type. * @param out Destination value. * @param a Left operand. * @param b Right operand. * @return errc::OVERFLOW on overflow, else errc::OK. */ template - requires(cuda::std::is_integral_v && cuda::std::is_signed_v) + requires(cuda::std::is_integral_v) __device__ inline errc ansi_add(T* out, T const* a, T const* b) { - using P = detail::promote; - auto r = static_cast

(*a) + static_cast

(*b); - if (r > static_cast

(cuda::std::numeric_limits::max()) || - r < static_cast

(cuda::std::numeric_limits::min())) { - return errc::OVERFLOW; - } - *out = static_cast(r); + T r; + if (cuda::add_overflow(r, *a, *b)) { return errc::OVERFLOW; } + *out = r; return errc::OK; } @@ -121,44 +99,21 @@ __device__ inline errc ansi_add(optional* out, optional const* a, optional } /** - * @brief Subtracts unsigned integral operands with overflow detection. + * @brief Subtracts integral operands with overflow detection. * - * @tparam T Unsigned integral type. + * @tparam T Integral type. * @param out Destination value. * @param a Minuend. * @param b Subtrahend. * @return errc::OVERFLOW on overflow, else errc::OK. */ template - requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) + requires(cuda::std::is_integral_v) __device__ inline errc ansi_sub(T* out, T const* a, T const* b) { - if (*a < *b) { return errc::OVERFLOW; } - auto r = *a - *b; - *out = static_cast(r); - return errc::OK; -} - -/** - * @brief Subtracts signed integral operands with overflow detection. - * - * @tparam T Signed integral type. - * @param out Destination value. - * @param a Minuend. - * @param b Subtrahend. - * @return errc::OVERFLOW on overflow, else errc::OK. - */ -template - requires(cuda::std::is_integral_v && cuda::std::is_signed_v) -__device__ inline errc ansi_sub(T* out, T const* a, T const* b) -{ - using P = detail::promote; - auto r = static_cast

(*a) - static_cast

(*b); - if (r > static_cast

(cuda::std::numeric_limits::max()) || - r < static_cast

(cuda::std::numeric_limits::min())) { - return errc::OVERFLOW; - } - *out = static_cast(r); + T r; + if (cuda::sub_overflow(r, *a, *b)) { return errc::OVERFLOW; } + *out = r; return errc::OK; } @@ -228,45 +183,21 @@ __device__ inline errc ansi_sub(optional* out, optional const* a, optional } /** - * @brief Multiplies unsigned integral operands with overflow detection. + * @brief Multiplies integral operands with overflow detection. * - * @tparam T Unsigned integral type. + * @tparam T Integral type. * @param out Destination value. * @param a Left operand. * @param b Right operand. * @return errc::OVERFLOW on overflow, else errc::OK. */ template - requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) + requires(cuda::std::is_integral_v) __device__ inline errc ansi_mul(T* out, T const* a, T const* b) { - using P = detail::promote; - auto r = static_cast

(*a) * static_cast

(*b); - if (r > static_cast

(cuda::std::numeric_limits::max())) { return errc::OVERFLOW; } - *out = static_cast(r); - return errc::OK; -} - -/** - * @brief Multiplies signed integral operands with overflow detection. - * - * @tparam T Signed integral type. - * @param out Destination value. - * @param a Left operand. - * @param b Right operand. - * @return errc::OVERFLOW on overflow, else errc::OK. - */ -template - requires(cuda::std::is_integral_v && cuda::std::is_signed_v) -__device__ inline errc ansi_mul(T* out, T const* a, T const* b) -{ - using P = detail::promote; - auto r = static_cast

(*a) * static_cast

(*b); - if (r > static_cast

(cuda::std::numeric_limits::max()) || - r < static_cast

(cuda::std::numeric_limits::min())) { - return errc::OVERFLOW; - } - *out = static_cast(r); + T r; + if (cuda::mul_overflow(r, *a, *b)) { return errc::OVERFLOW; } + *out = r; return errc::OK; } @@ -332,39 +263,21 @@ __device__ inline errc ansi_mul(optional* out, optional const* a, optional } /** - * @brief Divides unsigned integral operands with divide-by-zero checks. + * @brief Divides integral operands with divide-by-zero checks. * - * @tparam T Unsigned integral type. + * @tparam T Integral type. * @param out Destination value. * @param a Dividend. * @param b Divisor. * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. */ template - requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) + requires(cuda::std::is_integral_v) __device__ inline errc ansi_div(T* out, T const* a, T const* b) { - if (*b == 0) { return errc::DIVISION_BY_ZERO; } - *out = static_cast(static_cast(*a) / static_cast(*b)); - return errc::OK; -} - -/** - * @brief Divides signed integral operands with ANSI overflow and divide-by-zero checks. - * - * @tparam T Signed integral type. - * @param out Destination value. - * @param a Dividend. - * @param b Divisor. - * @return errc::DIVISION_BY_ZERO or errc::OVERFLOW on error, else errc::OK. - */ -template - requires(cuda::std::is_integral_v && cuda::std::is_signed_v) -__device__ inline errc ansi_div(T* out, T const* a, T const* b) -{ - if (*b == 0) { return errc::DIVISION_BY_ZERO; } - if (*a == cuda::std::numeric_limits::min() && *b == -1) { return errc::OVERFLOW; } - *out = static_cast(static_cast(*a) / static_cast(*b)); + T r; + if (cuda::div_overflow(r, *a, *b)) { return errc::DIVISION_BY_ZERO; } + *out = r; return errc::OK; } @@ -516,8 +429,6 @@ __device__ inline errc ansi_mod(double* out, double const* a, double const* b) template __device__ inline errc ansi_mod(decimal* out, decimal const* a, decimal const* b) { - if (b->value() == 0) { return errc::DIVISION_BY_ZERO; } - decimal div; if (errc e = ansi_div(&div, a, b); e != errc::OK) { return e; } From 05fa30d4205c256347680efd587c5ac4c7166db8 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Wed, 20 May 2026 21:03:33 +0000 Subject: [PATCH 15/34] fix: improve error handling in ansi_div for division by zero and overflow --- cpp/include/cudf/operators/ansi_arithmetic.cuh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index 14d05ba4a96c..e469753a6f6d 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -269,14 +269,15 @@ __device__ inline errc ansi_mul(optional* out, optional const* a, optional * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. + * @return errc::DIVISION_BY_ZERO on zero divisor, errc::OVERFLOW on overflow, else errc::OK. */ template requires(cuda::std::is_integral_v) __device__ inline errc ansi_div(T* out, T const* a, T const* b) { + if (*b == 0) { return errc::DIVISION_BY_ZERO; } T r; - if (cuda::div_overflow(r, *a, *b)) { return errc::DIVISION_BY_ZERO; } + if (cuda::div_overflow(r, *a, *b)) { return errc::OVERFLOW; } *out = r; return errc::OK; } @@ -310,9 +311,9 @@ __device__ inline errc ansi_div(T* out, T const* a, T const* b) template __device__ inline errc ansi_div(decimal* out, decimal const* a, decimal const* b) { - if (numeric::division_overflow(a->value(), b->value()) || b->value() == 0) { - return errc::OVERFLOW; - } + if (b->value() == 0) { return errc::DIVISION_BY_ZERO; } + + if (numeric::division_overflow(a->value(), b->value())) { return errc::OVERFLOW; } *out = decimal{numeric::scaled_integer{a->value() / b->value(), numeric::scale_type{a->scale() - b->scale()}}}; From 03c3ef912326dfd377f739a7eb076cdac7407903 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 21 May 2026 16:28:48 +0000 Subject: [PATCH 16/34] WARs for doxygen checks --- .../cudf/operators/ansi_arithmetic.cuh | 91 +++---------------- cpp/include/cudf/operators/arithmetic.cuh | 14 +-- cpp/include/cudf/operators/casts.cuh | 4 +- 3 files changed, 21 insertions(+), 88 deletions(-) diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index e469753a6f6d..9388bb1ae977 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -23,8 +23,8 @@ namespace ops { * @return errc::OVERFLOW on overflow, else errc::OK. */ template - requires(cuda::std::is_integral_v) __device__ inline errc ansi_add(T* out, T const* a, T const* b) + requires(cuda::std::is_integral_v) { T r; if (cuda::add_overflow(r, *a, *b)) { return errc::OVERFLOW; } @@ -36,14 +36,11 @@ __device__ inline errc ansi_add(T* out, T const* a, T const* b) * @brief Adds floating-point operands. * * @tparam T Floating-point type. - * @param out Destination value. - * @param a Left operand. - * @param b Right operand. * @return errc::OK. */ template - requires(cuda::std::is_floating_point_v) __device__ inline errc ansi_add(T* out, T const* a, T const* b) + requires(cuda::std::is_floating_point_v) { *out = *a + *b; return errc::OK; @@ -53,9 +50,6 @@ __device__ inline errc ansi_add(T* out, T const* a, T const* b) * @brief Adds fixed-point decimal operands with overflow detection. * * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Left operand. - * @param b Right operand. * @return errc::OVERFLOW on overflow, else errc::OK. */ template @@ -76,9 +70,6 @@ __device__ inline errc ansi_add(decimal* out, decimal const* a, decimal * @brief Adds optional operands with ANSI overflow behavior. * * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Left optional operand. - * @param b Right optional operand. * @return Operation status from underlying add, or errc::OK. */ template @@ -108,8 +99,8 @@ __device__ inline errc ansi_add(optional* out, optional const* a, optional * @return errc::OVERFLOW on overflow, else errc::OK. */ template - requires(cuda::std::is_integral_v) __device__ inline errc ansi_sub(T* out, T const* a, T const* b) + requires(cuda::std::is_integral_v) { T r; if (cuda::sub_overflow(r, *a, *b)) { return errc::OVERFLOW; } @@ -121,14 +112,11 @@ __device__ inline errc ansi_sub(T* out, T const* a, T const* b) * @brief Subtracts floating-point operands. * * @tparam T Floating-point type. - * @param out Destination value. - * @param a Minuend. - * @param b Subtrahend. * @return errc::OK. */ template - requires(cuda::std::is_floating_point_v) __device__ inline errc ansi_sub(T* out, T const* a, T const* b) + requires(cuda::std::is_floating_point_v) { *out = *a - *b; return errc::OK; @@ -138,9 +126,6 @@ __device__ inline errc ansi_sub(T* out, T const* a, T const* b) * @brief Subtracts fixed-point decimal operands with overflow detection. * * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Minuend. - * @param b Subtrahend. * @return errc::OVERFLOW on overflow, else errc::OK. */ template @@ -161,9 +146,6 @@ __device__ inline errc ansi_sub(decimal* out, decimal const* a, decimal * @brief Subtracts optional operands with ANSI overflow behavior. * * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional minuend. - * @param b Optional subtrahend. * @return Operation status from underlying subtract, or errc::OK. */ template @@ -192,8 +174,8 @@ __device__ inline errc ansi_sub(optional* out, optional const* a, optional * @return errc::OVERFLOW on overflow, else errc::OK. */ template - requires(cuda::std::is_integral_v) __device__ inline errc ansi_mul(T* out, T const* a, T const* b) + requires(cuda::std::is_integral_v) { T r; if (cuda::mul_overflow(r, *a, *b)) { return errc::OVERFLOW; } @@ -205,14 +187,11 @@ __device__ inline errc ansi_mul(T* out, T const* a, T const* b) * @brief Multiplies floating-point operands. * * @tparam T Floating-point type. - * @param out Destination value. - * @param a Left operand. - * @param b Right operand. * @return errc::OK. */ template - requires(cuda::std::is_floating_point_v) __device__ inline errc ansi_mul(T* out, T const* a, T const* b) + requires(cuda::std::is_floating_point_v) { *out = *a * *b; return errc::OK; @@ -222,9 +201,6 @@ __device__ inline errc ansi_mul(T* out, T const* a, T const* b) * @brief Multiplies fixed-point decimal operands with overflow detection. * * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Left operand. - * @param b Right operand. * @return errc::OVERFLOW on overflow, else errc::OK. */ template @@ -241,9 +217,6 @@ __device__ inline errc ansi_mul(decimal* out, decimal const* a, decimal * @brief Multiplies optional operands with ANSI overflow behavior. * * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Left optional operand. - * @param b Right optional operand. * @return Operation status from underlying multiply, or errc::OK. */ template @@ -272,8 +245,8 @@ __device__ inline errc ansi_mul(optional* out, optional const* a, optional * @return errc::DIVISION_BY_ZERO on zero divisor, errc::OVERFLOW on overflow, else errc::OK. */ template - requires(cuda::std::is_integral_v) __device__ inline errc ansi_div(T* out, T const* a, T const* b) + requires(cuda::std::is_integral_v) { if (*b == 0) { return errc::DIVISION_BY_ZERO; } T r; @@ -286,14 +259,11 @@ __device__ inline errc ansi_div(T* out, T const* a, T const* b) * @brief Divides floating-point operands. * * @tparam T Floating-point type. - * @param out Destination value. - * @param a Dividend. - * @param b Divisor. * @return errc::OK. */ template - requires(cuda::std::is_floating_point_v) __device__ inline errc ansi_div(T* out, T const* a, T const* b) + requires(cuda::std::is_floating_point_v) { *out = *a / *b; return errc::OK; @@ -303,9 +273,6 @@ __device__ inline errc ansi_div(T* out, T const* a, T const* b) * @brief Divides fixed-point decimal operands with ANSI checks. * * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Dividend. - * @param b Divisor. * @return errc::OVERFLOW on overflow or zero divisor, else errc::OK. */ template @@ -324,9 +291,6 @@ __device__ inline errc ansi_div(decimal* out, decimal const* a, decimal * @brief Divides optional operands with ANSI overflow behavior. * * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional dividend. - * @param b Optional divisor. * @return Operation status from underlying divide, or errc::OK. */ template @@ -355,8 +319,8 @@ __device__ inline errc ansi_div(optional* out, optional const* a, optional * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. */ template - requires(cuda::std::is_integral_v && cuda::std::is_signed_v) __device__ inline errc ansi_mod(T* out, T const* a, T const* b) + requires(cuda::std::is_integral_v && cuda::std::is_signed_v) { if (*b == 0) { return errc::DIVISION_BY_ZERO; } @@ -374,14 +338,11 @@ __device__ inline errc ansi_mod(T* out, T const* a, T const* b) * @brief Computes unsigned integral modulus with ANSI checks. * * @tparam T Unsigned integral type. - * @param out Destination value. - * @param a Dividend. - * @param b Divisor. * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. */ template - requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) __device__ inline errc ansi_mod(T* out, T const* a, T const* b) + requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) { if (*b == 0) { return errc::DIVISION_BY_ZERO; } *out = *a % *b; @@ -391,9 +352,6 @@ __device__ inline errc ansi_mod(T* out, T const* a, T const* b) /** * @brief Computes floating-point modulus for float operands with ANSI checks. * - * @param out Destination value. - * @param a Dividend. - * @param b Divisor. * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. */ __device__ inline errc ansi_mod(float* out, float const* a, float const* b) @@ -406,9 +364,6 @@ __device__ inline errc ansi_mod(float* out, float const* a, float const* b) /** * @brief Computes floating-point modulus for double operands with ANSI checks. * - * @param out Destination value. - * @param a Dividend. - * @param b Divisor. * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. */ __device__ inline errc ansi_mod(double* out, double const* a, double const* b) @@ -422,9 +377,6 @@ __device__ inline errc ansi_mod(double* out, double const* a, double const* b) * @brief Computes fixed-point decimal modulus with ANSI checks. * * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Dividend. - * @param b Divisor. * @return errc::DIVISION_BY_ZERO or propagated status, else errc::OK. */ template @@ -444,9 +396,6 @@ __device__ inline errc ansi_mod(decimal* out, decimal const* a, decimal * @brief Computes modulus for optional operands with ANSI behavior. * * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional dividend. - * @param b Optional divisor. * @return Operation status from underlying modulus, or errc::OK. */ template @@ -474,8 +423,8 @@ __device__ inline errc ansi_mod(optional* out, optional const* a, optional * @return errc::OVERFLOW on minimum representable input, else errc::OK. */ template - requires(cuda::std::is_integral_v && cuda::std::is_signed_v) __device__ inline errc ansi_abs(T* out, T const* a) + requires(cuda::std::is_integral_v && cuda::std::is_signed_v) { if (*a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } *out = (*a < 0) ? -(*a) : *a; @@ -486,13 +435,11 @@ __device__ inline errc ansi_abs(T* out, T const* a) * @brief Returns unsigned input unchanged for ANSI absolute value. * * @tparam T Unsigned integral type. - * @param out Destination value. - * @param a Input value. * @return errc::OK. */ template - requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) __device__ inline errc ansi_abs(T* out, T const* a) + requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) { *out = *a; return errc::OK; @@ -502,13 +449,11 @@ __device__ inline errc ansi_abs(T* out, T const* a) * @brief Computes absolute value for floating-point inputs. * * @tparam T Floating-point type. - * @param out Destination value. - * @param a Input value. * @return errc::OK. */ template - requires(cuda::std::is_floating_point_v) __device__ inline errc ansi_abs(T* out, T const* a) + requires(cuda::std::is_floating_point_v) { *out = (*a < 0) ? -(*a) : *a; return errc::OK; @@ -518,8 +463,6 @@ __device__ inline errc ansi_abs(T* out, T const* a) * @brief Computes absolute value for decimal inputs with ANSI overflow checks. * * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Input decimal value. * @return errc::OVERFLOW on minimum representable input, else errc::OK. */ template @@ -535,8 +478,6 @@ __device__ inline errc ansi_abs(decimal* out, decimal const* a) * @brief Computes absolute value for optional inputs with ANSI overflow checks. * * @tparam T Value type. - * @param out Destination optional value. - * @param a Optional input value. * @return Operation status from underlying abs, or errc::OK. */ template @@ -564,8 +505,8 @@ __device__ inline errc ansi_abs(optional* out, optional const* a) * @return errc::OVERFLOW on minimum representable input, else errc::OK. */ template - requires(cuda::std::is_signed_v) __device__ inline errc ansi_neg(T* out, T const* a) + requires(cuda::std::is_signed_v) { if (*a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } *out = -(*a); @@ -576,8 +517,6 @@ __device__ inline errc ansi_neg(T* out, T const* a) * @brief Computes unary negation for decimal inputs with ANSI overflow checks. * * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Input decimal value. * @return errc::OVERFLOW on minimum representable input, else errc::OK. */ template @@ -593,8 +532,6 @@ __device__ inline errc ansi_neg(decimal* out, decimal const* a) * @brief Computes unary negation for optional inputs with ANSI checks. * * @tparam T Signed type. - * @param out Destination optional value. - * @param a Optional input value. * @return Operation status from underlying negate, or errc::OK. */ template diff --git a/cpp/include/cudf/operators/arithmetic.cuh b/cpp/include/cudf/operators/arithmetic.cuh index 31434c86c4c5..dc03dda0b79f 100644 --- a/cpp/include/cudf/operators/arithmetic.cuh +++ b/cpp/include/cudf/operators/arithmetic.cuh @@ -19,8 +19,8 @@ namespace ops { * @return errc::OK. */ template - requires(cuda::std::is_signed_v || cuda::std::is_floating_point_v) __device__ inline errc abs(T* out, T const* a) + requires(cuda::std::is_signed_v || cuda::std::is_floating_point_v) { *out = (*a < 0) ? -*a : *a; return errc::OK; @@ -30,13 +30,11 @@ __device__ inline errc abs(T* out, T const* a) * @brief Returns unsigned input unchanged for absolute value. * * @tparam T Unsigned input and output type. - * @param out Destination value. - * @param a Input value. * @return errc::OK. */ template - requires(cuda::std::is_unsigned_v) __device__ inline errc abs(T* out, T const* a) + requires(cuda::std::is_unsigned_v) { *out = *a; return errc::OK; @@ -46,8 +44,6 @@ __device__ inline errc abs(T* out, T const* a) * @brief Computes absolute value for fixed-point decimal values. * * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Input decimal value. * @return errc::OK. */ template @@ -165,8 +161,8 @@ __device__ inline errc div(optional* out, optional const* a, optional c * @return errc::OK. */ template - requires(cuda::std::is_integral_v) __device__ inline errc floor_div(T* out, T const* a, T const* b) + requires(cuda::std::is_integral_v) { *out = cudf::detail::integral_floor_div(*a, *b); return errc::OK; @@ -401,8 +397,8 @@ __device__ inline errc mul(optional* out, optional const* a, optional c * @return errc::OK. */ template - requires(cuda::std::is_signed_v) __device__ inline errc neg(T* out, T const* a) + requires(cuda::std::is_signed_v) { *out = -(*a); return errc::OK; @@ -493,8 +489,8 @@ __device__ inline errc sub(optional* out, optional const* a, optional c * @return errc::OK. */ template - requires(cuda::std::is_floating_point_v || cuda::std::is_integral_v) __device__ inline errc true_div(double* out, T const* a, T const* b) + requires(cuda::std::is_floating_point_v || cuda::std::is_integral_v) { *out = static_cast(*a) / static_cast(*b); return errc::OK; diff --git a/cpp/include/cudf/operators/casts.cuh b/cpp/include/cudf/operators/casts.cuh index f18da8ae076a..2583a1e35e6a 100644 --- a/cpp/include/cudf/operators/casts.cuh +++ b/cpp/include/cudf/operators/casts.cuh @@ -352,8 +352,8 @@ __device__ inline errc cast_to_u64(optional* out, optional const* a * @return errc::OK. */ template - requires(std::is_integral_v || std::is_floating_point_v) __device__ inline errc cast_to_f32(float* out, T const* a) + requires(std::is_integral_v || std::is_floating_point_v) { *out = static_cast(*a); return errc::OK; @@ -404,8 +404,8 @@ __device__ inline errc cast_to_f32(optional* out, optional const* a) * @return errc::OK. */ template - requires(std::is_integral_v || std::is_floating_point_v) __device__ inline errc cast_to_f64(double* out, T const* a) + requires(std::is_integral_v || std::is_floating_point_v) { *out = static_cast(*a); return errc::OK; From 84032a41d1549caec19d1e13dd5406f4954f8e86 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 21 May 2026 16:32:18 +0000 Subject: [PATCH 17/34] refactor: remove unused promote header --- .../cudf/operators/ansi_arithmetic.cuh | 1 - cpp/include/cudf/operators/detail/promote.cuh | 66 ------------------- 2 files changed, 67 deletions(-) delete mode 100644 cpp/include/cudf/operators/detail/promote.cuh diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index 9388bb1ae977..69700299eb91 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -4,7 +4,6 @@ */ #pragma once -#include #include #include diff --git a/cpp/include/cudf/operators/detail/promote.cuh b/cpp/include/cudf/operators/detail/promote.cuh deleted file mode 100644 index 825a47d0926f..000000000000 --- a/cpp/include/cudf/operators/detail/promote.cuh +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. - * SPDX-License-Identifier: Apache-2.0 - */ -#pragma once - -#include - -#include - -namespace CUDF_EXPORT cudf { -namespace ops::detail { - -/** - * @brief Type promotion map used for overflow-safe integral arithmetic. - * - * @tparam T Input integral type. - */ -template -struct promoted_t; - -template <> -struct promoted_t { - using type = int16_t; -}; - -template <> -struct promoted_t { - using type = uint16_t; -}; - -template <> -struct promoted_t { - using type = int32_t; -}; - -template <> -struct promoted_t { - using type = uint32_t; -}; - -template <> -struct promoted_t { - using type = int64_t; -}; - -template <> -struct promoted_t { - using type = uint64_t; -}; - -template <> -struct promoted_t { - using type = __int128; -}; - -template <> -struct promoted_t { - using type = unsigned __int128; -}; - -template -using promote = typename promoted_t::type; - -} // namespace ops::detail -} // namespace CUDF_EXPORT cudf From 92ef3e9d0b73aeade3ee5decea925b4084c64a1c Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 21 May 2026 20:03:21 +0000 Subject: [PATCH 18/34] refactor: change enum to enum class for better type safety in error handling --- cpp/include/cudf/operators/error.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cudf/operators/error.hpp b/cpp/include/cudf/operators/error.hpp index 59258c4461ea..f72544a515f2 100644 --- a/cpp/include/cudf/operators/error.hpp +++ b/cpp/include/cudf/operators/error.hpp @@ -10,7 +10,7 @@ namespace CUDF_EXPORT cudf { namespace ops { -enum errc : int { OK = 0, OVERFLOW = 1, DIVISION_BY_ZERO = 2 }; +enum class errc : int { OK = 0, OVERFLOW = 1, DIVISION_BY_ZERO = 2 }; } // namespace ops } // namespace CUDF_EXPORT cudf From fae557c98b793049f8a536a2d0bec5964a21277b Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Fri, 22 May 2026 07:48:00 +0000 Subject: [PATCH 19/34] Refactor null handling and trigonometric operations to use cuda::std::optional - Updated functions in null_handling.cuh and trigonometric.cuh to replace custom optional handling with cuda::std::optional. - Removed the types.cuh file as it was no longer needed after the refactor. - Changed return types of several functions from errc to void, simplifying the interface. - Ensured null handling is consistent across all operations by using cuda::std::nullopt. --- cpp/include/cudf/fixed_point/fixed_point.hpp | 5 +- .../cudf/operators/ansi_arithmetic.cuh | 401 +++++++++--------- cpp/include/cudf/operators/arithmetic.cuh | 168 +++----- cpp/include/cudf/operators/bitwise.cuh | 74 ++-- cpp/include/cudf/operators/casts.cuh | 187 +++----- cpp/include/cudf/operators/comparison.cuh | 76 ++-- cpp/include/cudf/operators/error.hpp | 5 +- cpp/include/cudf/operators/identity.cuh | 28 ++ cpp/include/cudf/operators/logic.cuh | 86 ++-- cpp/include/cudf/operators/math.cuh | 211 +++------ cpp/include/cudf/operators/null_handling.cuh | 47 +- cpp/include/cudf/operators/trigonometric.cuh | 244 +++-------- cpp/include/cudf/operators/types.cuh | 93 ---- 13 files changed, 595 insertions(+), 1030 deletions(-) create mode 100644 cpp/include/cudf/operators/identity.cuh delete mode 100644 cpp/include/cudf/operators/types.cuh diff --git a/cpp/include/cudf/fixed_point/fixed_point.hpp b/cpp/include/cudf/fixed_point/fixed_point.hpp index 6dcc4aed20a1..3bce59d5cfcd 100644 --- a/cpp/include/cudf/fixed_point/fixed_point.hpp +++ b/cpp/include/cudf/fixed_point/fixed_point.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2020-2026, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */ @@ -784,6 +784,9 @@ CUDF_HOST_DEVICE inline fixed_point operator%(fixed_point{scaled_integer{remainder, scale}}; } +template +using decimal = + fixed_point; ///< decimal fixed point with user defined representation type using decimal32 = fixed_point; ///< 32-bit decimal fixed point using decimal64 = fixed_point; ///< 64-bit decimal fixed point using decimal128 = fixed_point<__int128_t, Radix::BASE_10>; ///< 128-bit decimal fixed point diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index 69700299eb91..3b882bae6726 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -4,10 +4,15 @@ */ #pragma once +#include +#include #include -#include +#include #include +#include +#include +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -19,40 +24,42 @@ namespace ops { * @param out Destination value. * @param a Left operand. * @param b Right operand. - * @return errc::OVERFLOW on overflow, else errc::OK. + * @return errc::OVERFLOW on overflow, else errc::SUCCESS. */ template -__device__ inline errc ansi_add(T* out, T const* a, T const* b) +__device__ errc ansi_add(T* out, T const* a, T const* b) requires(cuda::std::is_integral_v) { T r; if (cuda::add_overflow(r, *a, *b)) { return errc::OVERFLOW; } *out = r; - return errc::OK; + return errc::SUCCESS; } /** * @brief Adds floating-point operands. * * @tparam T Floating-point type. - * @return errc::OK. + * @return errc::SUCCESS. */ template -__device__ inline errc ansi_add(T* out, T const* a, T const* b) +__device__ errc ansi_add(T* out, T const* a, T const* b) requires(cuda::std::is_floating_point_v) { *out = *a + *b; - return errc::OK; + return errc::SUCCESS; } /** * @brief Adds fixed-point decimal operands with overflow detection. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on overflow, else errc::OK. + * @return errc::OVERFLOW on overflow, else errc::SUCCESS. */ template -__device__ inline errc ansi_add(decimal* out, decimal const* a, decimal const* b) +__device__ errc ansi_add(numeric::decimal* out, + numeric::decimal const* a, + numeric::decimal const* b) { auto scale = cuda::std::min(a->scale(), b->scale()); @@ -60,32 +67,34 @@ __device__ inline errc ansi_add(decimal* out, decimal const* a, decimal return errc::OVERFLOW; } - *out = decimal{numeric::scaled_integer{ + *out = numeric::decimal{numeric::scaled_integer{ a->rescaled(scale).value() + b->rescaled(scale).value(), numeric::scale_type{scale}}}; - return errc::OK; + return errc::SUCCESS; } /** * @brief Adds optional operands with ANSI overflow behavior. * * @tparam T Operand and result type. - * @return Operation status from underlying add, or errc::OK. + * @return Operation status from underlying add, or errc::SUCCESS. */ template -__device__ inline errc ansi_add(optional* out, optional const* a, optional const* b) +__device__ errc ansi_add(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; - if (errc e = ansi_add(&r, &a->value(), &b->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_add(&r, &a->value(), &b->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; return e; } *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; + return errc::SUCCESS; } /** @@ -95,40 +104,42 @@ __device__ inline errc ansi_add(optional* out, optional const* a, optional * @param out Destination value. * @param a Minuend. * @param b Subtrahend. - * @return errc::OVERFLOW on overflow, else errc::OK. + * @return errc::OVERFLOW on overflow, else errc::SUCCESS. */ template -__device__ inline errc ansi_sub(T* out, T const* a, T const* b) +__device__ errc ansi_sub(T* out, T const* a, T const* b) requires(cuda::std::is_integral_v) { T r; if (cuda::sub_overflow(r, *a, *b)) { return errc::OVERFLOW; } *out = r; - return errc::OK; + return errc::SUCCESS; } /** * @brief Subtracts floating-point operands. * * @tparam T Floating-point type. - * @return errc::OK. + * @return errc::SUCCESS. */ template -__device__ inline errc ansi_sub(T* out, T const* a, T const* b) +__device__ errc ansi_sub(T* out, T const* a, T const* b) requires(cuda::std::is_floating_point_v) { *out = *a - *b; - return errc::OK; + return errc::SUCCESS; } /** * @brief Subtracts fixed-point decimal operands with overflow detection. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on overflow, else errc::OK. + * @return errc::OVERFLOW on overflow, else errc::SUCCESS. */ template -__device__ inline errc ansi_sub(decimal* out, decimal const* a, decimal const* b) +__device__ errc ansi_sub(numeric::decimal* out, + numeric::decimal const* a, + numeric::decimal const* b) { auto scale = cuda::std::min(a->scale(), b->scale()); @@ -136,31 +147,33 @@ __device__ inline errc ansi_sub(decimal* out, decimal const* a, decimal return errc::OVERFLOW; } - *out = decimal{numeric::scaled_integer{ + *out = numeric::decimal{numeric::scaled_integer{ a->rescaled(scale).value() - b->rescaled(scale).value(), numeric::scale_type{scale}}}; - return errc::OK; + return errc::SUCCESS; } /** * @brief Subtracts optional operands with ANSI overflow behavior. * * @tparam T Operand and result type. - * @return Operation status from underlying subtract, or errc::OK. + * @return Operation status from underlying subtract, or errc::SUCCESS. */ template -__device__ inline errc ansi_sub(optional* out, optional const* a, optional const* b) +__device__ errc ansi_sub(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; - if (errc e = ansi_sub(&r, &a->value(), &b->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_sub(&r, &a->value(), &b->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; return e; } *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; + return errc::SUCCESS; } /** @@ -170,68 +183,72 @@ __device__ inline errc ansi_sub(optional* out, optional const* a, optional * @param out Destination value. * @param a Left operand. * @param b Right operand. - * @return errc::OVERFLOW on overflow, else errc::OK. + * @return errc::OVERFLOW on overflow, else errc::SUCCESS. */ template -__device__ inline errc ansi_mul(T* out, T const* a, T const* b) +__device__ errc ansi_mul(T* out, T const* a, T const* b) requires(cuda::std::is_integral_v) { T r; if (cuda::mul_overflow(r, *a, *b)) { return errc::OVERFLOW; } *out = r; - return errc::OK; + return errc::SUCCESS; } /** * @brief Multiplies floating-point operands. * * @tparam T Floating-point type. - * @return errc::OK. + * @return errc::SUCCESS. */ template -__device__ inline errc ansi_mul(T* out, T const* a, T const* b) +__device__ errc ansi_mul(T* out, T const* a, T const* b) requires(cuda::std::is_floating_point_v) { *out = *a * *b; - return errc::OK; + return errc::SUCCESS; } /** * @brief Multiplies fixed-point decimal operands with overflow detection. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on overflow, else errc::OK. + * @return errc::OVERFLOW on overflow, else errc::SUCCESS. */ template -__device__ inline errc ansi_mul(decimal* out, decimal const* a, decimal const* b) +__device__ errc ansi_mul(numeric::decimal* out, + numeric::decimal const* a, + numeric::decimal const* b) { if (numeric::multiplication_overflow(a->value(), b->value())) { return errc::OVERFLOW; } - *out = decimal{numeric::scaled_integer{a->value() * b->value(), - numeric::scale_type{a->scale() + b->scale()}}}; - return errc::OK; + *out = numeric::decimal{numeric::scaled_integer{ + a->value() * b->value(), numeric::scale_type{a->scale() + b->scale()}}}; + return errc::SUCCESS; } /** * @brief Multiplies optional operands with ANSI overflow behavior. * * @tparam T Operand and result type. - * @return Operation status from underlying multiply, or errc::OK. + * @return Operation status from underlying multiply, or errc::SUCCESS. */ template -__device__ inline errc ansi_mul(optional* out, optional const* a, optional const* b) +__device__ errc ansi_mul(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; - if (errc e = ansi_mul(&r, &a->value(), &b->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_mul(&r, &a->value(), &b->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; return e; } *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; + return errc::SUCCESS; } /** @@ -241,71 +258,75 @@ __device__ inline errc ansi_mul(optional* out, optional const* a, optional * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::DIVISION_BY_ZERO on zero divisor, errc::OVERFLOW on overflow, else errc::OK. + * @return errc::DIVISION_BY_ZERO on zero divisor, errc::OVERFLOW on overflow, else errc::SUCCESS. */ template -__device__ inline errc ansi_div(T* out, T const* a, T const* b) +__device__ errc ansi_div(T* out, T const* a, T const* b) requires(cuda::std::is_integral_v) { if (*b == 0) { return errc::DIVISION_BY_ZERO; } T r; if (cuda::div_overflow(r, *a, *b)) { return errc::OVERFLOW; } *out = r; - return errc::OK; + return errc::SUCCESS; } /** * @brief Divides floating-point operands. * * @tparam T Floating-point type. - * @return errc::OK. + * @return errc::SUCCESS. */ template -__device__ inline errc ansi_div(T* out, T const* a, T const* b) +__device__ errc ansi_div(T* out, T const* a, T const* b) requires(cuda::std::is_floating_point_v) { *out = *a / *b; - return errc::OK; + return errc::SUCCESS; } /** * @brief Divides fixed-point decimal operands with ANSI checks. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on overflow or zero divisor, else errc::OK. + * @return errc::OVERFLOW on overflow or zero divisor, else errc::SUCCESS. */ template -__device__ inline errc ansi_div(decimal* out, decimal const* a, decimal const* b) +__device__ errc ansi_div(numeric::decimal* out, + numeric::decimal const* a, + numeric::decimal const* b) { if (b->value() == 0) { return errc::DIVISION_BY_ZERO; } if (numeric::division_overflow(a->value(), b->value())) { return errc::OVERFLOW; } - *out = decimal{numeric::scaled_integer{a->value() / b->value(), - numeric::scale_type{a->scale() - b->scale()}}}; - return errc::OK; + *out = numeric::decimal{numeric::scaled_integer{ + a->value() / b->value(), numeric::scale_type{a->scale() - b->scale()}}}; + return errc::SUCCESS; } /** * @brief Divides optional operands with ANSI overflow behavior. * * @tparam T Operand and result type. - * @return Operation status from underlying divide, or errc::OK. + * @return Operation status from underlying divide, or errc::SUCCESS. */ template -__device__ inline errc ansi_div(optional* out, optional const* a, optional const* b) +__device__ errc ansi_div(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; - if (errc e = ansi_div(&r, &a->value(), &b->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_div(&r, &a->value(), &b->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; return e; } *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; + return errc::SUCCESS; } /** @@ -315,10 +336,10 @@ __device__ inline errc ansi_div(optional* out, optional const* a, optional * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. + * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::SUCCESS. */ template -__device__ inline errc ansi_mod(T* out, T const* a, T const* b) +__device__ errc ansi_mod(T* out, T const* a, T const* b) requires(cuda::std::is_integral_v && cuda::std::is_signed_v) { if (*b == 0) { return errc::DIVISION_BY_ZERO; } @@ -326,91 +347,95 @@ __device__ inline errc ansi_mod(T* out, T const* a, T const* b) // avoid signed overflow UB / trap for minimum value divided by -1. if (*a == cuda::std::numeric_limits::min() && *b == T{-1}) { *out = T{0}; - return errc::OK; + return errc::SUCCESS; } *out = *a % *b; - return errc::OK; + return errc::SUCCESS; } /** * @brief Computes unsigned integral modulus with ANSI checks. * * @tparam T Unsigned integral type. - * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. + * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::SUCCESS. */ template -__device__ inline errc ansi_mod(T* out, T const* a, T const* b) +__device__ errc ansi_mod(T* out, T const* a, T const* b) requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) { if (*b == 0) { return errc::DIVISION_BY_ZERO; } *out = *a % *b; - return errc::OK; + return errc::SUCCESS; } /** * @brief Computes floating-point modulus for float operands with ANSI checks. * - * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. + * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::SUCCESS. */ -__device__ inline errc ansi_mod(float* out, float const* a, float const* b) +__device__ errc ansi_mod(float* out, float const* a, float const* b) { if (*b == 0) { return errc::DIVISION_BY_ZERO; } *out = (*a) - (*b) * ::floorf((*a) / (*b)); - return errc::OK; + return errc::SUCCESS; } /** * @brief Computes floating-point modulus for double operands with ANSI checks. * - * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::OK. + * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::SUCCESS. */ -__device__ inline errc ansi_mod(double* out, double const* a, double const* b) +__device__ errc ansi_mod(double* out, double const* a, double const* b) { if (*b == 0) { return errc::DIVISION_BY_ZERO; } *out = (*a) - (*b) * ::floor((*a) / (*b)); - return errc::OK; + return errc::SUCCESS; } /** * @brief Computes fixed-point decimal modulus with ANSI checks. * * @tparam R Decimal representation type. - * @return errc::DIVISION_BY_ZERO or propagated status, else errc::OK. + * @return errc::DIVISION_BY_ZERO or propagated status, else errc::SUCCESS. */ template -__device__ inline errc ansi_mod(decimal* out, decimal const* a, decimal const* b) +__device__ errc ansi_mod(numeric::decimal* out, + numeric::decimal const* a, + numeric::decimal const* b) { - decimal div; + numeric::decimal div; - if (errc e = ansi_div(&div, a, b); e != errc::OK) { return e; } + if (errc e = ansi_div(&div, a, b); e != errc::SUCCESS) { return e; } - decimal quotient; + numeric::decimal quotient; floor("ient, &div); *out = *a - *b * quotient; - return errc::OK; + return errc::SUCCESS; } /** * @brief Computes modulus for optional operands with ANSI behavior. * * @tparam T Operand and result type. - * @return Operation status from underlying modulus, or errc::OK. + * @return Operation status from underlying modulus, or errc::SUCCESS. */ template -__device__ inline errc ansi_mod(optional* out, optional const* a, optional const* b) +__device__ errc ansi_mod(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; - if (errc e = ansi_mod(&r, &a->value(), &b->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_mod(&r, &a->value(), &b->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; return e; } *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; + return errc::SUCCESS; } /** @@ -419,80 +444,80 @@ __device__ inline errc ansi_mod(optional* out, optional const* a, optional * @tparam T Signed integral type. * @param out Destination value. * @param a Input value. - * @return errc::OVERFLOW on minimum representable input, else errc::OK. + * @return errc::OVERFLOW on minimum representable input, else errc::SUCCESS. */ template -__device__ inline errc ansi_abs(T* out, T const* a) +__device__ errc ansi_abs(T* out, T const* a) requires(cuda::std::is_integral_v && cuda::std::is_signed_v) { if (*a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } *out = (*a < 0) ? -(*a) : *a; - return errc::OK; + return errc::SUCCESS; } /** * @brief Returns unsigned input unchanged for ANSI absolute value. * * @tparam T Unsigned integral type. - * @return errc::OK. + * @return errc::SUCCESS. */ template -__device__ inline errc ansi_abs(T* out, T const* a) +__device__ errc ansi_abs(T* out, T const* a) requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) { *out = *a; - return errc::OK; + return errc::SUCCESS; } /** * @brief Computes absolute value for floating-point inputs. * * @tparam T Floating-point type. - * @return errc::OK. + * @return errc::SUCCESS. */ template -__device__ inline errc ansi_abs(T* out, T const* a) +__device__ errc ansi_abs(T* out, T const* a) requires(cuda::std::is_floating_point_v) { *out = (*a < 0) ? -(*a) : *a; - return errc::OK; + return errc::SUCCESS; } /** * @brief Computes absolute value for decimal inputs with ANSI overflow checks. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on minimum representable input, else errc::OK. + * @return errc::OVERFLOW on minimum representable input, else errc::SUCCESS. */ template -__device__ inline errc ansi_abs(decimal* out, decimal const* a) +__device__ errc ansi_abs(numeric::decimal* out, numeric::decimal const* a) { if (a->value() == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } auto rep = a->value() < 0 ? -a->value() : a->value(); - *out = decimal{numeric::scaled_integer{rep, numeric::scale_type{a->scale()}}}; - return errc::OK; + *out = numeric::decimal{numeric::scaled_integer{rep, numeric::scale_type{a->scale()}}}; + return errc::SUCCESS; } /** * @brief Computes absolute value for optional inputs with ANSI overflow checks. * * @tparam T Value type. - * @return Operation status from underlying abs, or errc::OK. + * @return Operation status from underlying abs, or errc::SUCCESS. */ template -__device__ inline errc ansi_abs(optional* out, optional const* a) +__device__ errc ansi_abs(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; - if (errc e = ansi_abs(&r, &a->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_abs(&r, &a->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; return e; } *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; + return errc::SUCCESS; } /** @@ -501,52 +526,52 @@ __device__ inline errc ansi_abs(optional* out, optional const* a) * @tparam T Signed type. * @param out Destination value. * @param a Input value. - * @return errc::OVERFLOW on minimum representable input, else errc::OK. + * @return errc::OVERFLOW on minimum representable input, else errc::SUCCESS. */ template -__device__ inline errc ansi_neg(T* out, T const* a) +__device__ errc ansi_neg(T* out, T const* a) requires(cuda::std::is_signed_v) { if (*a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } *out = -(*a); - return errc::OK; + return errc::SUCCESS; } /** * @brief Computes unary negation for decimal inputs with ANSI overflow checks. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on minimum representable input, else errc::OK. + * @return errc::OVERFLOW on minimum representable input, else errc::SUCCESS. */ template -__device__ inline errc ansi_neg(decimal* out, decimal const* a) +__device__ errc ansi_neg(numeric::decimal* out, numeric::decimal const* a) { if (a->value() == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } auto rep = -a->value(); - *out = decimal{numeric::scaled_integer{rep, numeric::scale_type{a->scale()}}}; - return errc::OK; + *out = numeric::decimal{numeric::scaled_integer{rep, numeric::scale_type{a->scale()}}}; + return errc::SUCCESS; } /** * @brief Computes unary negation for optional inputs with ANSI checks. * * @tparam T Signed type. - * @return Operation status from underlying negate, or errc::OK. + * @return Operation status from underlying negate, or errc::SUCCESS. */ template -__device__ inline errc ansi_neg(optional* out, optional const* a) +__device__ errc ansi_neg(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; - if (errc e = ansi_neg(&r, &a->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_neg(&r, &a->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; return e; } *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; + return errc::SUCCESS; } /** @@ -556,12 +581,12 @@ __device__ inline errc ansi_neg(optional* out, optional const* a) * @param out Destination decimal value. * @param a Input decimal value. * @param precision Maximum allowed precision. - * @return errc::OVERFLOW when precision is invalid or exceeded, else errc::OK. + * @return errc::OVERFLOW when precision is invalid or exceeded, else errc::SUCCESS. */ template -__device__ inline errc ansi_precision_check(decimal* out, - decimal const* a, - int32_t const* precision) +__device__ errc ansi_precision_check(numeric::decimal* out, + numeric::decimal const* a, + int32_t const* precision) { if (*precision <= 0) { return errc::OVERFLOW; } @@ -570,10 +595,12 @@ __device__ inline errc ansi_precision_check(decimal* out, auto abs_value = value < 0 ? -value : value; - if (abs_value >= detail::ipow10(static_cast(*precision))) { return errc::OVERFLOW; } + if (abs_value >= numeric::detail::ipow(static_cast(*precision))) { + return errc::OVERFLOW; + } *out = *a; - return errc::OK; + return errc::SUCCESS; } /** @@ -583,25 +610,25 @@ __device__ inline errc ansi_precision_check(decimal* out, * @param out Destination optional value. * @param a Optional decimal input. * @param precision Precision. - * @return Operation status from underlying precision check, or errc::OK. + * @return Operation status from underlying precision check, or errc::SUCCESS. */ template -__device__ inline errc ansi_precision_check(optional* out, - optional const* a, - optional const* precision) +__device__ errc ansi_precision_check(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* precision) { if (a->has_value() && precision->has_value()) { T r; - if (errc e = ansi_precision_check(&r, &a->value(), &precision->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_precision_check(&r, &a->value(), &precision->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; return e; } else { *out = r; - return errc::OK; + return errc::SUCCESS; } } else { - *out = nullopt; - return errc::OK; + *out = cuda::std::nullopt; + return errc::SUCCESS; } } @@ -612,22 +639,22 @@ __device__ inline errc ansi_precision_check(optional* out, * @param out Destination optional value. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc ansi_try_add(optional* out, optional const* a, optional const* b) +__device__ void ansi_try_add(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; - if (errc e = ansi_add(&r, &a->value(), &b->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_add(&r, &a->value(), &b->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; } else { *out = r; } } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -637,22 +664,22 @@ __device__ inline errc ansi_try_add(optional* out, optional const* a, opti * @param out Destination optional value. * @param a Optional minuend. * @param b Optional subtrahend. - * @return errc::OK. */ template -__device__ inline errc ansi_try_sub(optional* out, optional const* a, optional const* b) +__device__ void ansi_try_sub(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; - if (errc e = ansi_sub(&r, &a->value(), &b->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_sub(&r, &a->value(), &b->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; } else { *out = r; } } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -662,22 +689,22 @@ __device__ inline errc ansi_try_sub(optional* out, optional const* a, opti * @param out Destination optional value. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc ansi_try_mul(optional* out, optional const* a, optional const* b) +__device__ void ansi_try_mul(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; - if (errc e = ansi_mul(&r, &a->value(), &b->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_mul(&r, &a->value(), &b->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; } else { *out = r; } } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -687,22 +714,22 @@ __device__ inline errc ansi_try_mul(optional* out, optional const* a, opti * @param out Destination optional value. * @param a Optional dividend. * @param b Optional divisor. - * @return errc::OK. */ template -__device__ inline errc ansi_try_div(optional* out, optional const* a, optional const* b) +__device__ void ansi_try_div(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; - if (errc e = ansi_div(&r, &a->value(), &b->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_div(&r, &a->value(), &b->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; } else { *out = r; } } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -712,22 +739,22 @@ __device__ inline errc ansi_try_div(optional* out, optional const* a, opti * @param out Destination optional value. * @param a Optional dividend. * @param b Optional divisor. - * @return errc::OK. */ template -__device__ inline errc ansi_try_mod(optional* out, optional const* a, optional const* b) +__device__ void ansi_try_mod(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; - if (errc e = ansi_mod(&r, &a->value(), &b->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_mod(&r, &a->value(), &b->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; } else { *out = r; } } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -736,22 +763,20 @@ __device__ inline errc ansi_try_mod(optional* out, optional const* a, opti * @tparam T Operand and result type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc ansi_try_abs(optional* out, optional const* a) +__device__ void ansi_try_abs(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; - if (errc e = ansi_abs(&r, &a->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_abs(&r, &a->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; } else { *out = r; } } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -760,22 +785,20 @@ __device__ inline errc ansi_try_abs(optional* out, optional const* a) * @tparam T Operand and result type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc ansi_try_neg(optional* out, optional const* a) +__device__ void ansi_try_neg(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; - if (errc e = ansi_neg(&r, &a->value()); e != errc::OK) { - *out = nullopt; + if (errc e = ansi_neg(&r, &a->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; } else { *out = r; } } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -785,24 +808,22 @@ __device__ inline errc ansi_try_neg(optional* out, optional const* a) * @param out Destination optional decimal value. * @param a Optional decimal input. * @param precision Optional precision. - * @return errc::OK. */ template -__device__ inline errc ansi_try_precision_check(optional>* out, - optional> const* a, - optional const* precision) +__device__ void ansi_try_precision_check(cuda::std::optional>* out, + cuda::std::optional> const* a, + cuda::std::optional const* precision) { if (a->has_value() && precision->has_value()) { - decimal r; - if (errc e = ansi_precision_check(&r, &a->value(), &precision->value()); e != errc::OK) { - *out = nullopt; + numeric::decimal r; + if (errc e = ansi_precision_check(&r, &a->value(), &precision->value()); e != errc::SUCCESS) { + *out = cuda::std::nullopt; } else { *out = r; } } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } } // namespace ops diff --git a/cpp/include/cudf/operators/arithmetic.cuh b/cpp/include/cudf/operators/arithmetic.cuh index dc03dda0b79f..f47f36d7dbcf 100644 --- a/cpp/include/cudf/operators/arithmetic.cuh +++ b/cpp/include/cudf/operators/arithmetic.cuh @@ -5,7 +5,11 @@ #pragma once #include -#include +#include +#include + +#include +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -16,42 +20,36 @@ namespace ops { * @tparam T Input and output type. * @param out Destination value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc abs(T* out, T const* a) +__device__ void abs(T* out, T const* a) requires(cuda::std::is_signed_v || cuda::std::is_floating_point_v) { *out = (*a < 0) ? -*a : *a; - return errc::OK; } /** * @brief Returns unsigned input unchanged for absolute value. * * @tparam T Unsigned input and output type. - * @return errc::OK. */ template -__device__ inline errc abs(T* out, T const* a) +__device__ void abs(T* out, T const* a) requires(cuda::std::is_unsigned_v) { *out = *a; - return errc::OK; } /** * @brief Computes absolute value for fixed-point decimal values. * * @tparam R Decimal representation type. - * @return errc::OK. */ template -__device__ inline errc abs(decimal* out, decimal const* a) +__device__ void abs(numeric::decimal* out, numeric::decimal const* a) { auto rep = a->value() < 0 ? -a->value() : a->value(); - *out = decimal{numeric::scaled_integer{rep, a->scale()}}; - return errc::OK; + *out = numeric::decimal{numeric::scaled_integer{rep, a->scale()}}; } /** @@ -60,19 +58,17 @@ __device__ inline errc abs(decimal* out, decimal const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc abs(optional* out, optional const* a) +__device__ void abs(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; abs(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -82,13 +78,11 @@ __device__ inline errc abs(optional* out, optional const* a) * @param out Destination value. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc add(T* out, T const* a, T const* b) +__device__ void add(T* out, T const* a, T const* b) { *out = (*a + *b); - return errc::OK; } /** @@ -98,19 +92,19 @@ __device__ inline errc add(T* out, T const* a, T const* b) * @param out Destination optional value. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc add(optional* out, optional const* a, optional const* b) +__device__ void add(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; add(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -120,13 +114,11 @@ __device__ inline errc add(optional* out, optional const* a, optional c * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ template -__device__ inline errc div(T* out, T const* a, T const* b) +__device__ void div(T* out, T const* a, T const* b) { *out = (*a / *b); - return errc::OK; } /** @@ -136,19 +128,19 @@ __device__ inline errc div(T* out, T const* a, T const* b) * @param out Destination optional value. * @param a Optional dividend. * @param b Optional divisor. - * @return errc::OK. */ template -__device__ inline errc div(optional* out, optional const* a, optional const* b) +__device__ void div(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; div(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -158,14 +150,12 @@ __device__ inline errc div(optional* out, optional const* a, optional c * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ template -__device__ inline errc floor_div(T* out, T const* a, T const* b) +__device__ void floor_div(T* out, T const* a, T const* b) requires(cuda::std::is_integral_v) { *out = cudf::detail::integral_floor_div(*a, *b); - return errc::OK; } /** @@ -174,12 +164,10 @@ __device__ inline errc floor_div(T* out, T const* a, T const* b) * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ -__device__ inline errc floor_div(float* out, float const* a, float const* b) +__device__ inline void floor_div(float* out, float const* a, float const* b) { *out = ::floorf(*a / *b); - return errc::OK; } /** @@ -188,12 +176,10 @@ __device__ inline errc floor_div(float* out, float const* a, float const* b) * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ -__device__ inline errc floor_div(double* out, double const* a, double const* b) +__device__ inline void floor_div(double* out, double const* a, double const* b) { *out = ::floor(*a / *b); - return errc::OK; } /** @@ -203,19 +189,19 @@ __device__ inline errc floor_div(double* out, double const* a, double const* b) * @param out Destination optional value. * @param a Optional dividend. * @param b Optional divisor. - * @return errc::OK. */ template -__device__ inline errc floor_div(optional* out, optional const* a, optional const* b) +__device__ void floor_div(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; floor_div(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -225,13 +211,11 @@ __device__ inline errc floor_div(optional* out, optional const* a, optiona * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ template -__device__ inline errc mod(T* out, T const* a, T const* b) +__device__ void mod(T* out, T const* a, T const* b) { *out = (*a % *b); - return errc::OK; } /** @@ -240,13 +224,8 @@ __device__ inline errc mod(T* out, T const* a, T const* b) * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ -__device__ inline errc mod(float* out, float const* a, float const* b) -{ - *out = ::fmodf(*a, *b); - return errc::OK; -} +__device__ inline void mod(float* out, float const* a, float const* b) { *out = ::fmodf(*a, *b); } /** * @brief Computes floating-point remainder for double operands. @@ -254,13 +233,8 @@ __device__ inline errc mod(float* out, float const* a, float const* b) * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ -__device__ inline errc mod(double* out, double const* a, double const* b) -{ - *out = ::fmod(*a, *b); - return errc::OK; -} +__device__ inline void mod(double* out, double const* a, double const* b) { *out = ::fmod(*a, *b); } /** * @brief Computes remainder for optional operands. @@ -269,19 +243,19 @@ __device__ inline errc mod(double* out, double const* a, double const* b) * @param out Destination optional value. * @param a Optional dividend. * @param b Optional divisor. - * @return errc::OK. */ template -__device__ inline errc mod(optional* out, optional const* a, optional const* b) +__device__ void mod(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; mod(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -291,13 +265,11 @@ __device__ inline errc mod(optional* out, optional const* a, optional c * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ template -__device__ inline errc pymod(T* out, T const* a, T const* b) +__device__ void pymod(T* out, T const* a, T const* b) { *out = (*a % *b + *b) % *b; - return errc::OK; } /** @@ -306,12 +278,10 @@ __device__ inline errc pymod(T* out, T const* a, T const* b) * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ -__device__ inline errc pymod(float* out, float const* a, float const* b) +__device__ inline void pymod(float* out, float const* a, float const* b) { *out = ::fmodf(::fmodf(*a, *b) + *b, *b); - return errc::OK; } /** @@ -320,12 +290,10 @@ __device__ inline errc pymod(float* out, float const* a, float const* b) * @param out Destination value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ -__device__ inline errc pymod(double* out, double const* a, double const* b) +__device__ inline void pymod(double* out, double const* a, double const* b) { *out = ::fmod(::fmod(*a, *b) + *b, *b); - return errc::OK; } /** @@ -335,19 +303,19 @@ __device__ inline errc pymod(double* out, double const* a, double const* b) * @param out Destination optional value. * @param a Optional dividend. * @param b Optional divisor. - * @return errc::OK. */ template -__device__ inline errc pymod(optional* out, optional const* a, optional const* b) +__device__ void pymod(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; pymod(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -357,13 +325,11 @@ __device__ inline errc pymod(optional* out, optional const* a, optional * @param out Destination value. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc mul(T* out, T const* a, T const* b) +__device__ void mul(T* out, T const* a, T const* b) { *out = (*a * *b); - return errc::OK; } /** @@ -373,19 +339,19 @@ __device__ inline errc mul(T* out, T const* a, T const* b) * @param out Destination optional value. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc mul(optional* out, optional const* a, optional const* b) +__device__ void mul(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; mul(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -394,14 +360,12 @@ __device__ inline errc mul(optional* out, optional const* a, optional c * @tparam T Signed input and output type. * @param out Destination value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc neg(T* out, T const* a) +__device__ void neg(T* out, T const* a) requires(cuda::std::is_signed_v) { *out = -(*a); - return errc::OK; } /** @@ -410,14 +374,12 @@ __device__ inline errc neg(T* out, T const* a) * @tparam Rep Decimal representation type. * @param out Destination decimal value. * @param a Input decimal value. - * @return errc::OK. */ template -__device__ inline errc neg(decimal* out, decimal const* a) +__device__ void neg(numeric::decimal* out, numeric::decimal const* a) { auto rep = -a->value(); - *out = decimal{numeric::scaled_integer{rep, a->scale()}}; - return errc::OK; + *out = numeric::decimal{numeric::scaled_integer{rep, a->scale()}}; } /** @@ -426,19 +388,17 @@ __device__ inline errc neg(decimal* out, decimal const* a) * @tparam T Signed input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc neg(optional* out, optional const* a) +__device__ void neg(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; neg(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -448,13 +408,11 @@ __device__ inline errc neg(optional* out, optional const* a) * @param out Destination value. * @param a Minuend. * @param b Subtrahend. - * @return errc::OK. */ template -__device__ inline errc sub(T* out, T const* a, T const* b) +__device__ void sub(T* out, T const* a, T const* b) { *out = *a - *b; - return errc::OK; } /** @@ -464,19 +422,19 @@ __device__ inline errc sub(T* out, T const* a, T const* b) * @param out Destination optional value. * @param a Optional minuend. * @param b Optional subtrahend. - * @return errc::OK. */ template -__device__ inline errc sub(optional* out, optional const* a, optional const* b) +__device__ void sub(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; sub(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -486,14 +444,12 @@ __device__ inline errc sub(optional* out, optional const* a, optional c * @param out Destination double value. * @param a Dividend. * @param b Divisor. - * @return errc::OK. */ template -__device__ inline errc true_div(double* out, T const* a, T const* b) +__device__ void true_div(double* out, T const* a, T const* b) requires(cuda::std::is_floating_point_v || cuda::std::is_integral_v) { *out = static_cast(*a) / static_cast(*b); - return errc::OK; } /** @@ -503,19 +459,19 @@ __device__ inline errc true_div(double* out, T const* a, T const* b) * @param out Destination optional double value. * @param a Optional dividend. * @param b Optional divisor. - * @return errc::OK. */ template -__device__ inline errc true_div(optional* out, optional const* a, optional const* b) +__device__ void true_div(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { double r; true_div(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } } // namespace ops diff --git a/cpp/include/cudf/operators/bitwise.cuh b/cpp/include/cudf/operators/bitwise.cuh index bd1f0919a985..18612ee8cb8e 100644 --- a/cpp/include/cudf/operators/bitwise.cuh +++ b/cpp/include/cudf/operators/bitwise.cuh @@ -4,7 +4,9 @@ */ #pragma once -#include +#include + +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -16,13 +18,11 @@ namespace ops { * @param out Destination for the computed value. * @param a Left input operand. * @param b Right input operand. - * @return errc::OK. */ template -__device__ inline errc bit_and(T* out, T const* a, T const* b) +__device__ void bit_and(T* out, T const* a, T const* b) { *out = (*a & *b); - return errc::OK; } /** @@ -32,19 +32,19 @@ __device__ inline errc bit_and(T* out, T const* a, T const* b) * @param out Destination optional result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc bit_and(optional* out, optional const* a, optional const* b) +__device__ void bit_and(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; bit_and(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -53,13 +53,11 @@ __device__ inline errc bit_and(optional* out, optional const* a, optional< * @tparam T Operand and result type. * @param out Destination for the computed value. * @param a Input operand. - * @return errc::OK. */ template -__device__ inline errc bit_invert(T* out, T const* a) +__device__ void bit_invert(T* out, T const* a) { *out = ~(*a); - return errc::OK; } /** @@ -68,19 +66,17 @@ __device__ inline errc bit_invert(T* out, T const* a) * @tparam T Operand and result type. * @param out Destination optional result. * @param a Optional input operand. - * @return errc::OK. */ template -__device__ inline errc bit_invert(optional* out, optional const* a) +__device__ void bit_invert(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; bit_invert(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -90,13 +86,11 @@ __device__ inline errc bit_invert(optional* out, optional const* a) * @param out Destination for the computed value. * @param a Left input operand. * @param b Right input operand. - * @return errc::OK. */ template -__device__ inline errc bit_or(T* out, T const* a, T const* b) +__device__ void bit_or(T* out, T const* a, T const* b) { *out = (*a | *b); - return errc::OK; } /** @@ -106,19 +100,19 @@ __device__ inline errc bit_or(T* out, T const* a, T const* b) * @param out Destination optional result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc bit_or(optional* out, optional const* a, optional const* b) +__device__ void bit_or(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; bit_or(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -128,13 +122,11 @@ __device__ inline errc bit_or(optional* out, optional const* a, optional -__device__ inline errc bit_xor(T* out, T const* a, T const* b) +__device__ void bit_xor(T* out, T const* a, T const* b) { *out = (*a ^ *b); - return errc::OK; } /** @@ -144,19 +136,19 @@ __device__ inline errc bit_xor(T* out, T const* a, T const* b) * @param out Destination optional result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc bit_xor(optional* out, optional const* a, optional const* b) +__device__ void bit_xor(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; bit_xor(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -166,13 +158,11 @@ __device__ inline errc bit_xor(optional* out, optional const* a, optional< * @param out Destination for the computed value. * @param a Input value. * @param b Shift count. - * @return errc::OK. */ template -__device__ inline errc bit_shift_left(T* out, T const* a, T const* b) +__device__ void bit_shift_left(T* out, T const* a, T const* b) { *out = (*a << *b); - return errc::OK; } /** @@ -182,19 +172,19 @@ __device__ inline errc bit_shift_left(T* out, T const* a, T const* b) * @param out Destination optional result. * @param a Optional input value. * @param b Optional shift count. - * @return errc::OK. */ template -__device__ inline errc bit_shift_left(optional* out, optional const* a, optional const* b) +__device__ void bit_shift_left(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; bit_shift_left(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -204,13 +194,11 @@ __device__ inline errc bit_shift_left(optional* out, optional const* a, op * @param out Destination for the computed value. * @param a Input value. * @param b Shift count. - * @return errc::OK. */ template -__device__ inline errc bit_shift_right(T* out, T const* a, T const* b) +__device__ void bit_shift_right(T* out, T const* a, T const* b) { *out = (*a >> *b); - return errc::OK; } /** @@ -220,19 +208,19 @@ __device__ inline errc bit_shift_right(T* out, T const* a, T const* b) * @param out Destination optional result. * @param a Optional input value. * @param b Optional shift count. - * @return errc::OK. */ template -__device__ inline errc bit_shift_right(optional* out, optional const* a, optional const* b) +__device__ void bit_shift_right(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; bit_shift_right(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } } // namespace ops diff --git a/cpp/include/cudf/operators/casts.cuh b/cpp/include/cudf/operators/casts.cuh index 2583a1e35e6a..cf9767b86bf8 100644 --- a/cpp/include/cudf/operators/casts.cuh +++ b/cpp/include/cudf/operators/casts.cuh @@ -5,7 +5,11 @@ #pragma once #include -#include +#include +#include + +#include +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -17,13 +21,11 @@ namespace ops { * @tparam T Source type. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_b8(bool* out, T const* a) +__device__ void cast_to_b8(bool* out, T const* a) { *out = static_cast(*a); - return errc::OK; } /** @@ -32,19 +34,17 @@ __device__ inline errc cast_to_b8(bool* out, T const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_b8(optional* out, optional const* a) +__device__ void cast_to_b8(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { bool r; cast_to_b8(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -54,13 +54,11 @@ __device__ inline errc cast_to_b8(optional* out, optional const* a) * @tparam T Source type. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_i8(int8_t* out, T const* a) +__device__ void cast_to_i8(int8_t* out, T const* a) { *out = static_cast(*a); - return errc::OK; } /** @@ -69,19 +67,17 @@ __device__ inline errc cast_to_i8(int8_t* out, T const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_i8(optional* out, optional const* a) +__device__ void cast_to_i8(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { int8_t r; cast_to_i8(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -91,13 +87,11 @@ __device__ inline errc cast_to_i8(optional* out, optional const* a) * @tparam T Source type. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_i16(int16_t* out, T const* a) +__device__ void cast_to_i16(int16_t* out, T const* a) { *out = static_cast(*a); - return errc::OK; } /** @@ -106,19 +100,17 @@ __device__ inline errc cast_to_i16(int16_t* out, T const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_i16(optional* out, optional const* a) +__device__ void cast_to_i16(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { int16_t r; cast_to_i16(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -128,13 +120,11 @@ __device__ inline errc cast_to_i16(optional* out, optional const* a) * @tparam T Source type. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_i32(int32_t* out, T const* a) +__device__ void cast_to_i32(int32_t* out, T const* a) { *out = static_cast(*a); - return errc::OK; } /** @@ -143,19 +133,17 @@ __device__ inline errc cast_to_i32(int32_t* out, T const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_i32(optional* out, optional const* a) +__device__ void cast_to_i32(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { int32_t r; cast_to_i32(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -165,13 +153,11 @@ __device__ inline errc cast_to_i32(optional* out, optional const* a) * @tparam T Source type. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_i64(int64_t* out, T const* a) +__device__ void cast_to_i64(int64_t* out, T const* a) { *out = static_cast(*a); - return errc::OK; } /** @@ -180,19 +166,17 @@ __device__ inline errc cast_to_i64(int64_t* out, T const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_i64(optional* out, optional const* a) +__device__ void cast_to_i64(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { int64_t r; cast_to_i64(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -202,13 +186,11 @@ __device__ inline errc cast_to_i64(optional* out, optional const* a) * @tparam T Source type. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_u8(uint8_t* out, T const* a) +__device__ void cast_to_u8(uint8_t* out, T const* a) { *out = static_cast(*a); - return errc::OK; } /** @@ -217,19 +199,17 @@ __device__ inline errc cast_to_u8(uint8_t* out, T const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_u8(optional* out, optional const* a) +__device__ void cast_to_u8(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { uint8_t r; cast_to_u8(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -239,13 +219,11 @@ __device__ inline errc cast_to_u8(optional* out, optional const* a) * @tparam T Source type. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_u16(uint16_t* out, T const* a) +__device__ void cast_to_u16(uint16_t* out, T const* a) { *out = static_cast(*a); - return errc::OK; } /** @@ -254,19 +232,17 @@ __device__ inline errc cast_to_u16(uint16_t* out, T const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_u16(optional* out, optional const* a) +__device__ void cast_to_u16(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { uint16_t r; cast_to_u16(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -276,13 +252,11 @@ __device__ inline errc cast_to_u16(optional* out, optional const* a * @tparam T Source type. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_u32(uint32_t* out, T const* a) +__device__ void cast_to_u32(uint32_t* out, T const* a) { *out = static_cast(*a); - return errc::OK; } /** @@ -291,19 +265,17 @@ __device__ inline errc cast_to_u32(uint32_t* out, T const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_u32(optional* out, optional const* a) +__device__ void cast_to_u32(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { uint32_t r; cast_to_u32(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -313,13 +285,11 @@ __device__ inline errc cast_to_u32(optional* out, optional const* a * @tparam T Source type. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_u64(uint64_t* out, T const* a) +__device__ void cast_to_u64(uint64_t* out, T const* a) { *out = static_cast(*a); - return errc::OK; } /** @@ -328,19 +298,17 @@ __device__ inline errc cast_to_u64(uint64_t* out, T const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_u64(optional* out, optional const* a) +__device__ void cast_to_u64(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { uint64_t r; cast_to_u64(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -349,14 +317,12 @@ __device__ inline errc cast_to_u64(optional* out, optional const* a * Overloads support integral, floating-point, fixed-point decimal, and optional inputs. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_f32(float* out, T const* a) - requires(std::is_integral_v || std::is_floating_point_v) +__device__ void cast_to_f32(float* out, T const* a) + requires(cuda::std::is_integral_v || cuda::std::is_floating_point_v) { *out = static_cast(*a); - return errc::OK; } /** @@ -365,13 +331,11 @@ __device__ inline errc cast_to_f32(float* out, T const* a) * @tparam R Source decimal representation type. * @param out Destination cast value. * @param a Source decimal value. - * @return errc::OK. */ template -__device__ inline errc cast_to_f32(float* out, decimal const* a) +__device__ void cast_to_f32(float* out, numeric::decimal const* a) { *out = convert_fixed_to_floating(*a); - return errc::OK; } /** @@ -380,19 +344,17 @@ __device__ inline errc cast_to_f32(float* out, decimal const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_f32(optional* out, optional const* a) +__device__ void cast_to_f32(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { float r; cast_to_f32(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -401,14 +363,12 @@ __device__ inline errc cast_to_f32(optional* out, optional const* a) * Overloads support integral, floating-point, fixed-point decimal, and optional inputs. * @param out Destination cast value. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_f64(double* out, T const* a) - requires(std::is_integral_v || std::is_floating_point_v) +__device__ void cast_to_f64(double* out, T const* a) + requires(cuda::std::is_integral_v || cuda::std::is_floating_point_v) { *out = static_cast(*a); - return errc::OK; } /** @@ -417,13 +377,11 @@ __device__ inline errc cast_to_f64(double* out, T const* a) * @tparam R Source decimal representation type. * @param out Destination cast value. * @param a Source decimal value. - * @return errc::OK. */ template -__device__ inline errc cast_to_f64(double* out, decimal const* a) +__device__ void cast_to_f64(double* out, numeric::decimal const* a) { *out = convert_fixed_to_floating(*a); - return errc::OK; } /** @@ -432,19 +390,17 @@ __device__ inline errc cast_to_f64(double* out, decimal const* a) * @tparam T Source type. * @param out Destination optional cast value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_f64(optional* out, optional const* a) +__device__ void cast_to_f64(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { double r; cast_to_f64(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } namespace detail { @@ -456,19 +412,17 @@ namespace detail { * @tparam From Source representation type. * @param out Destination decimal value. * @param a Source decimal value. - * @return errc::OK. */ template -__device__ inline errc decimal_cast(decimal* out, decimal const* a) +__device__ void decimal_cast(numeric::decimal* out, numeric::decimal const* a) { auto rep = static_cast(a->value()); - *out = decimal{numeric::scaled_integer{rep, a->scale()}}; - return errc::OK; + *out = numeric::decimal{numeric::scaled_integer{rep, a->scale()}}; } } // namespace detail -// TODO: CAST_TO_DEC32 for int & float +// TODO(lamarrr): CAST_TO_DEC32 for int & float /** * @brief Casts decimal input values to decimal32. @@ -477,10 +431,9 @@ __device__ inline errc decimal_cast(decimal* out, decimal const* a) * @tparam R Source decimal representation type. * @param out Destination decimal32 value. * @param a Source decimal value. - * @return errc::OK. */ template -__device__ inline errc cast_to_dec32(numeric::decimal32* out, decimal const* a) +__device__ void cast_to_dec32(numeric::decimal32* out, numeric::decimal const* a) { return detail::decimal_cast(out, a); } @@ -491,20 +444,18 @@ __device__ inline errc cast_to_dec32(numeric::decimal32* out, decimal const* * @tparam R Source decimal representation type. * @param out Destination optional decimal32 value. * @param a Optional decimal input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_dec32(optional* out, - optional> const* a) +__device__ void cast_to_dec32(cuda::std::optional* out, + cuda::std::optional> const* a) { if (a->has_value()) { numeric::decimal32 r; cast_to_dec32(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -514,10 +465,9 @@ __device__ inline errc cast_to_dec32(optional* out, * @tparam R Source decimal representation type. * @param out Destination decimal64 value. * @param a Source decimal value. - * @return errc::OK. */ template -__device__ inline errc cast_to_dec64(numeric::decimal64* out, decimal const* a) +__device__ void cast_to_dec64(numeric::decimal64* out, numeric::decimal const* a) { return detail::decimal_cast(out, a); } @@ -528,20 +478,18 @@ __device__ inline errc cast_to_dec64(numeric::decimal64* out, decimal const* * @tparam R Source decimal representation type. * @param out Destination optional decimal64 value. * @param a Optional decimal input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_dec64(optional* out, - optional> const* a) +__device__ void cast_to_dec64(cuda::std::optional* out, + cuda::std::optional> const* a) { if (a->has_value()) { numeric::decimal64 r; cast_to_dec64(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -551,10 +499,9 @@ __device__ inline errc cast_to_dec64(optional* out, * @tparam R Source decimal representation type. * @param out Destination decimal128 value. * @param a Source decimal value. - * @return errc::OK. */ template -__device__ inline errc cast_to_dec128(numeric::decimal128* out, decimal const* a) +__device__ void cast_to_dec128(numeric::decimal128* out, numeric::decimal const* a) { return detail::decimal_cast(out, a); } @@ -565,20 +512,18 @@ __device__ inline errc cast_to_dec128(numeric::decimal128* out, decimal const * @tparam R Source decimal representation type. * @param out Destination optional decimal128 value. * @param a Optional decimal input value. - * @return errc::OK. */ template -__device__ inline errc cast_to_dec128(optional* out, - optional> const* a) +__device__ void cast_to_dec128(cuda::std::optional* out, + cuda::std::optional> const* a) { if (a->has_value()) { numeric::decimal128 r; cast_to_dec128(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -589,13 +534,13 @@ __device__ inline errc cast_to_dec128(optional* out, * @param out Destination decimal value. * @param a Source decimal value. * @param new_scale Target decimal scale. - * @return errc::OK. */ template -__device__ inline errc rescale(decimal* out, decimal const* a, int32_t const* new_scale) +__device__ void rescale(numeric::decimal* out, + numeric::decimal const* a, + int32_t const* new_scale) { *out = a->rescaled(numeric::scale_type{*new_scale}); - return errc::OK; } /** @@ -605,21 +550,19 @@ __device__ inline errc rescale(decimal* out, decimal const* a, int32_t con * @param out Destination optional decimal value. * @param a Optional source decimal value. * @param new_scale Optional target decimal scale. - * @return errc::OK. */ template -__device__ inline errc rescale(optional>* out, - optional> const* a, - optional const* new_scale) +__device__ void rescale(cuda::std::optional>* out, + cuda::std::optional> const* a, + cuda::std::optional const* new_scale) { if (a->has_value() && new_scale->has_value()) { - decimal r; + numeric::decimal r; rescale(&r, &a->value(), &new_scale->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } } // namespace ops diff --git a/cpp/include/cudf/operators/comparison.cuh b/cpp/include/cudf/operators/comparison.cuh index c22d96019950..f5a823affa2e 100644 --- a/cpp/include/cudf/operators/comparison.cuh +++ b/cpp/include/cudf/operators/comparison.cuh @@ -4,7 +4,9 @@ */ #pragma once -#include +#include + +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -16,13 +18,11 @@ namespace ops { * @param out Destination for the comparison result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc equal(bool* out, T const* a, T const* b) +__device__ void equal(bool* out, T const* a, T const* b) { *out = (*a == *b); - return errc::OK; } /** @@ -32,10 +32,11 @@ __device__ inline errc equal(bool* out, T const* a, T const* b) * @param out Destination optional boolean result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc equal(optional* out, optional const* a, optional const* b) +__device__ void equal(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { bool r; @@ -46,7 +47,6 @@ __device__ inline errc equal(optional* out, optional const* a, optional } else { *out = false; } - return errc::OK; } /** @@ -56,13 +56,11 @@ __device__ inline errc equal(optional* out, optional const* a, optional * @param out Destination for the comparison result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc not_equal(bool* out, T const* a, T const* b) +__device__ void not_equal(bool* out, T const* a, T const* b) { *out = (*a != *b); - return errc::OK; } /** @@ -72,10 +70,11 @@ __device__ inline errc not_equal(bool* out, T const* a, T const* b) * @param out Destination optional boolean result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc not_equal(optional* out, optional const* a, optional const* b) +__device__ void not_equal(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { bool r; @@ -86,7 +85,6 @@ __device__ inline errc not_equal(optional* out, optional const* a, opti } else { *out = true; } - return errc::OK; } /** @@ -96,13 +94,11 @@ __device__ inline errc not_equal(optional* out, optional const* a, opti * @param out Destination for the comparison result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc greater(bool* out, T const* a, T const* b) +__device__ void greater(bool* out, T const* a, T const* b) { *out = (*a > *b); - return errc::OK; } /** @@ -112,10 +108,11 @@ __device__ inline errc greater(bool* out, T const* a, T const* b) * @param out Destination optional boolean result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc greater(optional* out, optional const* a, optional const* b) +__device__ void greater(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { bool r; @@ -124,7 +121,6 @@ __device__ inline errc greater(optional* out, optional const* a, option } else { *out = false; } - return errc::OK; } /** @@ -134,13 +130,11 @@ __device__ inline errc greater(optional* out, optional const* a, option * @param out Destination for the comparison result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc greater_equal(bool* out, T const* a, T const* b) +__device__ void greater_equal(bool* out, T const* a, T const* b) { *out = (*a >= *b); - return errc::OK; } /** @@ -150,12 +144,11 @@ __device__ inline errc greater_equal(bool* out, T const* a, T const* b) * @param out Destination optional boolean result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc greater_equal(optional* out, - optional const* a, - optional const* b) +__device__ void greater_equal(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { bool r; @@ -164,7 +157,6 @@ __device__ inline errc greater_equal(optional* out, } else { *out = false; } - return errc::OK; } /** @@ -174,13 +166,11 @@ __device__ inline errc greater_equal(optional* out, * @param out Destination for the comparison result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc less(bool* out, T const* a, T const* b) +__device__ void less(bool* out, T const* a, T const* b) { *out = (*a < *b); - return errc::OK; } /** @@ -190,10 +180,11 @@ __device__ inline errc less(bool* out, T const* a, T const* b) * @param out Destination optional boolean result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc less(optional* out, optional const* a, optional const* b) +__device__ void less(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { bool r; @@ -202,7 +193,6 @@ __device__ inline errc less(optional* out, optional const* a, optional< } else { *out = false; } - return errc::OK; } /** @@ -212,13 +202,11 @@ __device__ inline errc less(optional* out, optional const* a, optional< * @param out Destination for the comparison result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc less_equal(bool* out, T const* a, T const* b) +__device__ void less_equal(bool* out, T const* a, T const* b) { *out = (*a <= *b); - return errc::OK; } /** @@ -228,10 +216,11 @@ __device__ inline errc less_equal(bool* out, T const* a, T const* b) * @param out Destination optional boolean result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc less_equal(optional* out, optional const* a, optional const* b) +__device__ void less_equal(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { bool r; @@ -240,7 +229,6 @@ __device__ inline errc less_equal(optional* out, optional const* a, opt } else { *out = false; } - return errc::OK; } /** @@ -250,13 +238,11 @@ __device__ inline errc less_equal(optional* out, optional const* a, opt * @param out Destination for the comparison result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc null_equal(bool* out, T const* a, T const* b) +__device__ void null_equal(bool* out, T const* a, T const* b) { *out = (*a == *b); - return errc::OK; } /** @@ -266,10 +252,11 @@ __device__ inline errc null_equal(bool* out, T const* a, T const* b) * @param out Destination optional boolean result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc null_equal(optional* out, optional const* a, optional const* b) +__device__ void null_equal(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { *out = (*(*a) == *(*b)); @@ -278,7 +265,6 @@ __device__ inline errc null_equal(optional* out, optional const* a, opt } else { *out = false; } - return errc::OK; } } // namespace ops diff --git a/cpp/include/cudf/operators/error.hpp b/cpp/include/cudf/operators/error.hpp index f72544a515f2..c087406bbcf1 100644 --- a/cpp/include/cudf/operators/error.hpp +++ b/cpp/include/cudf/operators/error.hpp @@ -1,4 +1,3 @@ - /* * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 @@ -7,10 +6,12 @@ #include +#include + namespace CUDF_EXPORT cudf { namespace ops { -enum class errc : int { OK = 0, OVERFLOW = 1, DIVISION_BY_ZERO = 2 }; +enum class errc : cuda::std::int8_t { SUCCESS = 0, OVERFLOW = 1, DIVISION_BY_ZERO = 2 }; } // namespace ops } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/identity.cuh b/cpp/include/cudf/operators/identity.cuh new file mode 100644 index 000000000000..e7cee2521402 --- /dev/null +++ b/cpp/include/cudf/operators/identity.cuh @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +/** + * @brief Copies an input value to the output. + * + * @tparam T Value type. + * @param out Destination value. + * @param a Input value. + */ +template +__device__ void identity(T* out, T const* a) +{ + *out = *a; +} + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/logic.cuh b/cpp/include/cudf/operators/logic.cuh index 8d4873380f48..09f293f55fa8 100644 --- a/cpp/include/cudf/operators/logic.cuh +++ b/cpp/include/cudf/operators/logic.cuh @@ -4,7 +4,9 @@ */ #pragma once -#include +#include + +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -16,13 +18,11 @@ namespace ops { * @param out Destination for the logical result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc null_logical_and(bool* out, T const* a, T const* b) +__device__ void null_logical_and(bool* out, T const* a, T const* b) { *out = (*a && *b); - return errc::OK; } /** @@ -32,27 +32,25 @@ __device__ inline errc null_logical_and(bool* out, T const* a, T const* b) * @param out Destination optional logical result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc null_logical_and(optional* out, - optional const* a, - optional const* b) +__device__ void null_logical_and(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { bool r; null_logical_and(&r, &a->value(), &b->value()); *out = r; } else if (!a->has_value() && !b->has_value()) { - *out = nullopt; + *out = cuda::std::nullopt; } else { if (a->has_value() ? *(*a) : *(*b)) { - *out = nullopt; + *out = cuda::std::nullopt; } else { *out = false; } } - return errc::OK; } /** @@ -62,13 +60,11 @@ __device__ inline errc null_logical_and(optional* out, * @param out Destination for the logical result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc null_logical_or(bool* out, T const* a, T const* b) +__device__ void null_logical_or(bool* out, T const* a, T const* b) { *out = (*a || *b); - return errc::OK; } /** @@ -78,27 +74,25 @@ __device__ inline errc null_logical_or(bool* out, T const* a, T const* b) * @param out Destination optional logical result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc null_logical_or(optional* out, - optional const* a, - optional const* b) +__device__ void null_logical_or(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { bool r; null_logical_or(&r, &a->value(), &b->value()); *out = r; } else if (!a->has_value() && !b->has_value()) { - *out = nullopt; + *out = cuda::std::nullopt; } else { if (a->has_value() ? *(*a) : *(*b)) { *out = true; } else { - *out = nullopt; + *out = cuda::std::nullopt; } } - return errc::OK; } /** @@ -108,13 +102,11 @@ __device__ inline errc null_logical_or(optional* out, * @param out Destination for the logical result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc logical_and(bool* out, T const* a, T const* b) +__device__ void logical_and(bool* out, T const* a, T const* b) { *out = (*a && *b); - return errc::OK; } /** @@ -124,19 +116,19 @@ __device__ inline errc logical_and(bool* out, T const* a, T const* b) * @param out Destination optional logical result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc logical_and(optional* out, optional const* a, optional const* b) +__device__ void logical_and(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { bool r; logical_and(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -146,13 +138,11 @@ __device__ inline errc logical_and(optional* out, optional const* a, op * @param out Destination for the logical result. * @param a Left operand. * @param b Right operand. - * @return errc::OK. */ template -__device__ inline errc logical_or(bool* out, T const* a, T const* b) +__device__ void logical_or(bool* out, T const* a, T const* b) { *out = (*a || *b); - return errc::OK; } /** @@ -162,19 +152,19 @@ __device__ inline errc logical_or(bool* out, T const* a, T const* b) * @param out Destination optional logical result. * @param a Left optional operand. * @param b Right optional operand. - * @return errc::OK. */ template -__device__ inline errc logical_or(optional* out, optional const* a, optional const* b) +__device__ void logical_or(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { bool r; logical_or(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -183,13 +173,11 @@ __device__ inline errc logical_or(optional* out, optional const* a, opt * @tparam T Operand type. * @param out Destination for the logical result. * @param a Input operand. - * @return errc::OK. */ template -__device__ inline errc logical_not(bool* out, T const* a) +__device__ void logical_not(bool* out, T const* a) { *out = !(*a); - return errc::OK; } /** @@ -198,19 +186,17 @@ __device__ inline errc logical_not(bool* out, T const* a) * @tparam T Operand type. * @param out Destination optional logical result. * @param a Optional input operand. - * @return errc::OK. */ template -__device__ inline errc logical_not(optional* out, optional const* a) +__device__ void logical_not(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { bool r; logical_not(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -221,13 +207,11 @@ __device__ inline errc logical_not(optional* out, optional const* a) * @param true_value Value selected when @p pred is true. * @param false_value Value selected when @p pred is false. * @param pred Selection predicate. - * @return errc::OK. */ template -__device__ inline errc if_else(T* out, T const* true_value, T const* false_value, bool const* pred) +__device__ void if_else(T* out, T const* true_value, T const* false_value, bool const* pred) { *out = *pred ? *true_value : *false_value; - return errc::OK; } /** @@ -238,22 +222,20 @@ __device__ inline errc if_else(T* out, T const* true_value, T const* false_value * @param true_value Optional value selected when @p pred is true. * @param false_value Optional value selected when @p pred is false. * @param pred Optional selection predicate. - * @return errc::OK. */ template -__device__ inline errc if_else(optional* out, - optional const* true_value, - optional const* false_value, - optional const* pred) +__device__ void if_else(cuda::std::optional* out, + cuda::std::optional const* true_value, + cuda::std::optional const* false_value, + cuda::std::optional const* pred) { if (pred->has_value() && true_value->has_value() && false_value->has_value()) { T r; if_else(&r, &true_value->value(), &false_value->value(), &pred->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } } // namespace ops diff --git a/cpp/include/cudf/operators/math.cuh b/cpp/include/cudf/operators/math.cuh index e6abd4187e4d..4aac00e9fe3e 100644 --- a/cpp/include/cudf/operators/math.cuh +++ b/cpp/include/cudf/operators/math.cuh @@ -4,7 +4,10 @@ */ #pragma once -#include +#include +#include + +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -15,26 +18,16 @@ namespace ops { * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc cbrt(float* out, float const* a) -{ - *out = ::cbrtf(*a); - return errc::OK; -} +__device__ inline void cbrt(float* out, float const* a) { *out = ::cbrtf(*a); } /** * @brief Computes cube root for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc cbrt(double* out, double const* a) -{ - *out = ::cbrt(*a); - return errc::OK; -} +__device__ inline void cbrt(double* out, double const* a) { *out = ::cbrt(*a); } /** * @brief Computes cube root for optional input. @@ -42,19 +35,17 @@ __device__ inline errc cbrt(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cbrt(optional* out, optional const* a) +__device__ void cbrt(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; cbrt(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -64,26 +55,16 @@ __device__ inline errc cbrt(optional* out, optional const* a) * overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc ceil(float* out, float const* a) -{ - *out = ::ceilf(*a); - return errc::OK; -} +__device__ inline void ceil(float* out, float const* a) { *out = ::ceilf(*a); } /** * @brief Computes ceiling for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc ceil(double* out, double const* a) -{ - *out = ::ceil(*a); - return errc::OK; -} +__device__ inline void ceil(double* out, double const* a) { *out = ::ceil(*a); } /** * @brief Computes ceiling for decimal input. @@ -91,25 +72,23 @@ __device__ inline errc ceil(double* out, double const* a) * @tparam R Decimal representation type. * @param out Destination decimal value. * @param a Input decimal value. - * @return errc::OK. */ template -__device__ inline errc ceil(decimal* out, decimal const* a) +__device__ void ceil(numeric::decimal* out, numeric::decimal const* a) { if (a->scale() >= 0) { *out = *a; - return errc::OK; - } - auto factor = detail::ipow10(-static_cast(a->scale())); - auto div = a->value() / factor; - auto rem = a->value() % factor; - if (rem == 0) { - *out = *a; } else { - auto val = a->value() > 0 ? (div + 1) : div; - *out = decimal{numeric::scaled_integer{val * factor, a->scale()}}; + auto factor = detail::ipow10(-static_cast(a->scale())); + auto div = a->value() / factor; + auto rem = a->value() % factor; + if (rem == 0) { + *out = *a; + } else { + auto val = a->value() > 0 ? (div + 1) : div; + *out = numeric::decimal{numeric::scaled_integer{val * factor, a->scale()}}; + } } - return errc::OK; } /** @@ -118,19 +97,17 @@ __device__ inline errc ceil(decimal* out, decimal const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc ceil(optional* out, optional const* a) +__device__ void ceil(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; ceil(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -139,26 +116,16 @@ __device__ inline errc ceil(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc exp(float* out, float const* a) -{ - *out = ::expf(*a); - return errc::OK; -} +__device__ inline void exp(float* out, float const* a) { *out = ::expf(*a); } /** * @brief Computes natural exponential for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc exp(double* out, double const* a) -{ - *out = ::exp(*a); - return errc::OK; -} +__device__ inline void exp(double* out, double const* a) { *out = ::exp(*a); } /** * @brief Computes natural exponential for optional input. @@ -166,19 +133,17 @@ __device__ inline errc exp(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc exp(optional* out, optional const* a) +__device__ void exp(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; exp(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -188,26 +153,16 @@ __device__ inline errc exp(optional* out, optional const* a) * overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc floor(float* out, float const* a) -{ - *out = ::floorf(*a); - return errc::OK; -} +__device__ inline void floor(float* out, float const* a) { *out = ::floorf(*a); } /** * @brief Computes floor for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc floor(double* out, double const* a) -{ - *out = ::floor(*a); - return errc::OK; -} +__device__ inline void floor(double* out, double const* a) { *out = ::floor(*a); } /** * @brief Computes floor for decimal input. @@ -215,25 +170,23 @@ __device__ inline errc floor(double* out, double const* a) * @tparam R Decimal representation type. * @param out Destination decimal value. * @param a Input decimal value. - * @return errc::OK. */ template -__device__ inline errc floor(decimal* out, decimal const* a) +__device__ void floor(numeric::decimal* out, numeric::decimal const* a) { if (a->scale() >= 0) { *out = *a; - return errc::OK; - } - auto factor = detail::ipow10(-static_cast(a->scale())); - auto div = a->value() / factor; - auto rem = a->value() % factor; - if (rem == 0) { - *out = *a; } else { - auto val = a->value() > 0 ? div : (div - 1); - *out = decimal{numeric::scaled_integer{val * factor, a->scale()}}; + auto factor = numeric::detail::ipow(-static_cast(a->scale())); + auto div = a->value() / factor; + auto rem = a->value() % factor; + if (rem == 0) { + *out = *a; + } else { + auto val = a->value() > 0 ? div : (div - 1); + *out = numeric::decimal{numeric::scaled_integer{val * factor, a->scale()}}; + } } - return errc::OK; } /** @@ -242,19 +195,17 @@ __device__ inline errc floor(decimal* out, decimal const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc floor(optional* out, optional const* a) +__device__ void floor(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; floor(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -263,26 +214,16 @@ __device__ inline errc floor(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc log(float* out, float const* a) -{ - *out = ::logf(*a); - return errc::OK; -} +__device__ inline void log(float* out, float const* a) { *out = ::logf(*a); } /** * @brief Computes natural logarithm for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc log(double* out, double const* a) -{ - *out = ::log(*a); - return errc::OK; -} +__device__ inline void log(double* out, double const* a) { *out = ::log(*a); } /** * @brief Computes natural logarithm for optional input. @@ -290,19 +231,17 @@ __device__ inline errc log(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc log(optional* out, optional const* a) +__device__ void log(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; log(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -312,13 +251,8 @@ __device__ inline errc log(optional* out, optional const* a) * @param out Destination for the computed value. * @param a Base value. * @param b Exponent value. - * @return errc::OK. */ -__device__ inline errc pow(float* out, float const* a, float const* b) -{ - *out = ::powf(*a, *b); - return errc::OK; -} +__device__ inline void pow(float* out, float const* a, float const* b) { *out = ::powf(*a, *b); } /** * @brief Computes exponentiation for double input. @@ -326,13 +260,8 @@ __device__ inline errc pow(float* out, float const* a, float const* b) * @param out Destination for the computed value. * @param a Base value. * @param b Exponent value. - * @return errc::OK. */ -__device__ inline errc pow(double* out, double const* a, double const* b) -{ - *out = ::pow(*a, *b); - return errc::OK; -} +__device__ inline void pow(double* out, double const* a, double const* b) { *out = ::pow(*a, *b); } /** * @brief Computes exponentiation for optional input. @@ -341,19 +270,19 @@ __device__ inline errc pow(double* out, double const* a, double const* b) * @param out Destination optional value. * @param a Optional base value. * @param b Optional exponent value. - * @return errc::OK. */ template -__device__ inline errc pow(optional* out, optional const* a, optional const* b) +__device__ void pow(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { T r; pow(&r, &a->value(), &b->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -362,26 +291,16 @@ __device__ inline errc pow(optional* out, optional const* a, optional c * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc rint(float* out, float const* a) -{ - *out = ::rintf(*a); - return errc::OK; -} +__device__ inline void rint(float* out, float const* a) { *out = ::rintf(*a); } /** * @brief Rounds to integral value for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc rint(double* out, double const* a) -{ - *out = ::rint(*a); - return errc::OK; -} +__device__ inline void rint(double* out, double const* a) { *out = ::rint(*a); } /** * @brief Rounds to integral value for optional input. @@ -389,19 +308,17 @@ __device__ inline errc rint(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc rint(optional* out, optional const* a) +__device__ void rint(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; rint(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -410,26 +327,16 @@ __device__ inline errc rint(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc sqrt(float* out, float const* a) -{ - *out = ::sqrtf(*a); - return errc::OK; -} +__device__ inline void sqrt(float* out, float const* a) { *out = ::sqrtf(*a); } /** * @brief Computes square root for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc sqrt(double* out, double const* a) -{ - *out = ::sqrt(*a); - return errc::OK; -} +__device__ inline void sqrt(double* out, double const* a) { *out = ::sqrt(*a); } /** * @brief Computes square root for optional input. @@ -437,19 +344,17 @@ __device__ inline errc sqrt(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc sqrt(optional* out, optional const* a) +__device__ void sqrt(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; sqrt(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } } // namespace ops diff --git a/cpp/include/cudf/operators/null_handling.cuh b/cpp/include/cudf/operators/null_handling.cuh index 3c21f2f98960..f74afde0c9fc 100644 --- a/cpp/include/cudf/operators/null_handling.cuh +++ b/cpp/include/cudf/operators/null_handling.cuh @@ -5,7 +5,9 @@ #pragma once -#include +#include + +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -16,13 +18,11 @@ namespace ops { * @tparam T Input type. * @param out Destination for the null test result. * @param a Input value. - * @return errc::OK. */ template -__device__ inline errc is_null(bool* out, T const* a) +__device__ void is_null(bool* out, T const* a) { *out = false; - return errc::OK; } /** @@ -31,13 +31,11 @@ __device__ inline errc is_null(bool* out, T const* a) * @tparam T Input value type. * @param out Destination optional boolean result. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc is_null(optional* out, optional const* a) +__device__ void is_null(cuda::std::optional* out, cuda::std::optional const* a) { *out = !a->has_value(); - return errc::OK; } /** @@ -47,23 +45,21 @@ __device__ inline errc is_null(optional* out, optional const* a) * @param out Destination optional value. * @param a Optional input value. * @param condition Optional boolean condition. - * @return errc::OK. */ template -__device__ inline errc nullify_if(optional* out, - optional const* a, - optional const* condition) +__device__ void nullify_if(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* condition) { if (condition->has_value() && a->has_value()) { if (condition->value()) { - *out = nullopt; + *out = cuda::std::nullopt; } else { *out = a->value(); } } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -73,13 +69,11 @@ __device__ inline errc nullify_if(optional* out, * @param out Destination value. * @param a First value. * @param b Second value. - * @return errc::OK. */ template -__device__ inline errc coalesce(T* out, T const* a, T const* b) +__device__ void coalesce(T* out, T const* a, T const* b) { *out = *a; - return errc::OK; } /** @@ -89,19 +83,19 @@ __device__ inline errc coalesce(T* out, T const* a, T const* b) * @param out Destination optional value. * @param a First optional value. * @param b Second optional value. - * @return errc::OK. */ template -__device__ inline errc coalesce(optional* out, optional const* a, optional const* b) +__device__ void coalesce(cuda::std::optional* out, + cuda::std::optional const* a, + cuda::std::optional const* b) { if (a->has_value()) { *out = a->value(); } else if (b->has_value()) { *out = b->value(); } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -109,29 +103,22 @@ __device__ inline errc coalesce(optional* out, optional const* a, optional * * @param out Destination boolean predicate. * @param a Input boolean predicate. - * @return errc::OK. */ -__device__ inline errc predicate(bool* out, bool const* a) -{ - *out = *a; - return errc::OK; -} +__device__ inline void predicate(bool* out, bool const* a) { *out = *a; } /** * @brief Converts an optional predicate to a non-nullable predicate. * * @param out Destination optional boolean predicate. * @param a Optional input boolean predicate. - * @return errc::OK. */ -__device__ inline errc predicate(optional* out, optional const* a) +__device__ inline void predicate(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { *out = a->value(); } else { *out = false; } - return errc::OK; } } // namespace ops diff --git a/cpp/include/cudf/operators/trigonometric.cuh b/cpp/include/cudf/operators/trigonometric.cuh index 42f254d28b4e..0f5dfda2600c 100644 --- a/cpp/include/cudf/operators/trigonometric.cuh +++ b/cpp/include/cudf/operators/trigonometric.cuh @@ -4,7 +4,9 @@ */ #pragma once -#include +#include + +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -15,26 +17,16 @@ namespace ops { * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arccos(float* out, float const* a) -{ - *out = ::acosf(*a); - return errc::OK; -} +__device__ inline void arccos(float* out, float const* a) { *out = ::acosf(*a); } /** * @brief Computes inverse cosine for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arccos(double* out, double const* a) -{ - *out = ::acos(*a); - return errc::OK; -} +__device__ inline void arccos(double* out, double const* a) { *out = ::acos(*a); } /** * @brief Computes inverse cosine for optional input. @@ -42,19 +34,17 @@ __device__ inline errc arccos(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc arccos(optional* out, optional const* a) +__device__ void arccos(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; arccos(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -63,26 +53,16 @@ __device__ inline errc arccos(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arccosh(float* out, float const* a) -{ - *out = ::acoshf(*a); - return errc::OK; -} +__device__ inline void arccosh(float* out, float const* a) { *out = ::acoshf(*a); } /** * @brief Computes inverse hyperbolic cosine for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arccosh(double* out, double const* a) -{ - *out = ::acosh(*a); - return errc::OK; -} +__device__ inline void arccosh(double* out, double const* a) { *out = ::acosh(*a); } /** * @brief Computes inverse hyperbolic cosine for optional input. @@ -90,19 +70,17 @@ __device__ inline errc arccosh(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc arccosh(optional* out, optional const* a) +__device__ void arccosh(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; arccosh(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -111,26 +89,16 @@ __device__ inline errc arccosh(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arcsin(float* out, float const* a) -{ - *out = ::asinf(*a); - return errc::OK; -} +__device__ inline void arcsin(float* out, float const* a) { *out = ::asinf(*a); } /** * @brief Computes inverse sine for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arcsin(double* out, double const* a) -{ - *out = ::asin(*a); - return errc::OK; -} +__device__ inline void arcsin(double* out, double const* a) { *out = ::asin(*a); } /** * @brief Computes inverse sine for optional input. @@ -138,19 +106,17 @@ __device__ inline errc arcsin(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc arcsin(optional* out, optional const* a) +__device__ void arcsin(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; arcsin(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -159,26 +125,16 @@ __device__ inline errc arcsin(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arcsinh(float* out, float const* a) -{ - *out = ::asinhf(*a); - return errc::OK; -} +__device__ inline void arcsinh(float* out, float const* a) { *out = ::asinhf(*a); } /** * @brief Computes inverse hyperbolic sine for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arcsinh(double* out, double const* a) -{ - *out = ::asinh(*a); - return errc::OK; -} +__device__ inline void arcsinh(double* out, double const* a) { *out = ::asinh(*a); } /** * @brief Computes inverse hyperbolic sine for optional input. @@ -186,19 +142,17 @@ __device__ inline errc arcsinh(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc arcsinh(optional* out, optional const* a) +__device__ void arcsinh(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; arcsinh(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -207,26 +161,16 @@ __device__ inline errc arcsinh(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arctan(float* out, float const* a) -{ - *out = ::atanf(*a); - return errc::OK; -} +__device__ inline void arctan(float* out, float const* a) { *out = ::atanf(*a); } /** * @brief Computes inverse tangent for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arctan(double* out, double const* a) -{ - *out = ::atan(*a); - return errc::OK; -} +__device__ inline void arctan(double* out, double const* a) { *out = ::atan(*a); } /** * @brief Computes inverse tangent for optional input. @@ -234,19 +178,17 @@ __device__ inline errc arctan(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc arctan(optional* out, optional const* a) +__device__ void arctan(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; arctan(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -255,26 +197,16 @@ __device__ inline errc arctan(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arctanh(float* out, float const* a) -{ - *out = ::atanhf(*a); - return errc::OK; -} +__device__ inline void arctanh(float* out, float const* a) { *out = ::atanhf(*a); } /** * @brief Computes inverse hyperbolic tangent for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc arctanh(double* out, double const* a) -{ - *out = ::atanh(*a); - return errc::OK; -} +__device__ inline void arctanh(double* out, double const* a) { *out = ::atanh(*a); } /** * @brief Computes inverse hyperbolic tangent for optional input. @@ -282,19 +214,17 @@ __device__ inline errc arctanh(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc arctanh(optional* out, optional const* a) +__device__ void arctanh(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; arctanh(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -303,26 +233,16 @@ __device__ inline errc arctanh(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc cos(float* out, float const* a) -{ - *out = ::cosf(*a); - return errc::OK; -} +__device__ inline void cos(float* out, float const* a) { *out = ::cosf(*a); } /** * @brief Computes cosine for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc cos(double* out, double const* a) -{ - *out = ::cos(*a); - return errc::OK; -} +__device__ inline void cos(double* out, double const* a) { *out = ::cos(*a); } /** * @brief Computes cosine for optional input. @@ -330,19 +250,17 @@ __device__ inline errc cos(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cos(optional* out, optional const* a) +__device__ void cos(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; cos(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -351,26 +269,16 @@ __device__ inline errc cos(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc cosh(float* out, float const* a) -{ - *out = ::coshf(*a); - return errc::OK; -} +__device__ inline void cosh(float* out, float const* a) { *out = ::coshf(*a); } /** * @brief Computes hyperbolic cosine for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc cosh(double* out, double const* a) -{ - *out = ::cosh(*a); - return errc::OK; -} +__device__ inline void cosh(double* out, double const* a) { *out = ::cosh(*a); } /** * @brief Computes hyperbolic cosine for optional input. @@ -378,19 +286,17 @@ __device__ inline errc cosh(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc cosh(optional* out, optional const* a) +__device__ void cosh(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; cosh(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -399,26 +305,16 @@ __device__ inline errc cosh(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc sin(float* out, float const* a) -{ - *out = ::sinf(*a); - return errc::OK; -} +__device__ inline void sin(float* out, float const* a) { *out = ::sinf(*a); } /** * @brief Computes sine for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc sin(double* out, double const* a) -{ - *out = ::sin(*a); - return errc::OK; -} +__device__ inline void sin(double* out, double const* a) { *out = ::sin(*a); } /** * @brief Computes sine for optional input. @@ -426,19 +322,17 @@ __device__ inline errc sin(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc sin(optional* out, optional const* a) +__device__ void sin(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; sin(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -447,26 +341,16 @@ __device__ inline errc sin(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc sinh(float* out, float const* a) -{ - *out = ::sinhf(*a); - return errc::OK; -} +__device__ inline void sinh(float* out, float const* a) { *out = ::sinhf(*a); } /** * @brief Computes hyperbolic sine for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc sinh(double* out, double const* a) -{ - *out = ::sinh(*a); - return errc::OK; -} +__device__ inline void sinh(double* out, double const* a) { *out = ::sinh(*a); } /** * @brief Computes hyperbolic sine for optional input. @@ -474,19 +358,17 @@ __device__ inline errc sinh(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc sinh(optional* out, optional const* a) +__device__ void sinh(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; sinh(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -495,26 +377,16 @@ __device__ inline errc sinh(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc tan(float* out, float const* a) -{ - *out = ::tanf(*a); - return errc::OK; -} +__device__ inline void tan(float* out, float const* a) { *out = ::tanf(*a); } /** * @brief Computes tangent for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc tan(double* out, double const* a) -{ - *out = ::tan(*a); - return errc::OK; -} +__device__ inline void tan(double* out, double const* a) { *out = ::tan(*a); } /** * @brief Computes tangent for optional input. @@ -522,19 +394,17 @@ __device__ inline errc tan(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc tan(optional* out, optional const* a) +__device__ void tan(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; tan(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } /** @@ -543,26 +413,16 @@ __device__ inline errc tan(optional* out, optional const* a) * Scalar overloads support float and double inputs, and an optional overload propagates nulls. * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc tanh(float* out, float const* a) -{ - *out = ::tanhf(*a); - return errc::OK; -} +__device__ inline void tanh(float* out, float const* a) { *out = ::tanhf(*a); } /** * @brief Computes hyperbolic tangent for double input. * * @param out Destination for the computed value. * @param a Input value. - * @return errc::OK. */ -__device__ inline errc tanh(double* out, double const* a) -{ - *out = ::tanh(*a); - return errc::OK; -} +__device__ inline void tanh(double* out, double const* a) { *out = ::tanh(*a); } /** * @brief Computes hyperbolic tangent for optional input. @@ -570,19 +430,17 @@ __device__ inline errc tanh(double* out, double const* a) * @tparam T Input and output type. * @param out Destination optional value. * @param a Optional input value. - * @return errc::OK. */ template -__device__ inline errc tanh(optional* out, optional const* a) +__device__ void tanh(cuda::std::optional* out, cuda::std::optional const* a) { if (a->has_value()) { T r; tanh(&r, &a->value()); *out = r; } else { - *out = nullopt; + *out = cuda::std::nullopt; } - return errc::OK; } } // namespace ops diff --git a/cpp/include/cudf/operators/types.cuh b/cpp/include/cudf/operators/types.cuh deleted file mode 100644 index f71611170042..000000000000 --- a/cpp/include/cudf/operators/types.cuh +++ /dev/null @@ -1,93 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. - * SPDX-License-Identifier: Apache-2.0 - */ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace CUDF_EXPORT cudf { -namespace ops { - -template -using optional = cuda::std::optional; - -inline constexpr auto nullopt = cuda::std::nullopt; - -template -using decimal = numeric::fixed_point; - -template -using duration = cuda::std::chrono::duration; - -namespace detail { - -/** - * @brief Computes an integer power of ten. - * - * @tparam T Integral exponent and return type. - * @param exponent Non-negative exponent value. - * @return Ten raised to @p exponent. - * @pre exponent >= 0. - */ -template -__device__ constexpr T ipow10(T exponent) -{ - if (exponent == 0) { return 1; } - - T extra = 1; - T square = 10; - T n = exponent; - - while (n > 1) { - if ((n & 1) == 1) { extra *= square; } - n >>= 1; - square *= square; - } - - return square * extra; -} - -} // namespace detail - -/** - * @brief Copies an input value to the output. - * - * @tparam T Value type. - * @param out Destination value. - * @param a Input value. - * @return errc::OK. - */ -template -__device__ inline errc identity(T* out, T const* a) -{ - *out = *a; - return errc::OK; -} - -/** - * @brief Copies an optional input value to the output. - * - * @tparam T Value type. - * @param out Destination optional value. - * @param a Optional input value. - * @return errc::OK. - */ -template -__device__ inline errc identity(optional* out, optional const* a) -{ - *out = *a; - return errc::OK; -} - -} // namespace ops -} // namespace CUDF_EXPORT cudf From fc4553177a870e094dad18303d0704f68dfa278a Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Fri, 22 May 2026 09:06:15 +0000 Subject: [PATCH 20/34] fix if_else logic --- cpp/include/cudf/operators/logic.cuh | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cpp/include/cudf/operators/logic.cuh b/cpp/include/cudf/operators/logic.cuh index 09f293f55fa8..97b9c61a5e8e 100644 --- a/cpp/include/cudf/operators/logic.cuh +++ b/cpp/include/cudf/operators/logic.cuh @@ -215,13 +215,13 @@ __device__ void if_else(T* out, T const* true_value, T const* false_value, bool } /** - * @brief Selects one of two optional values based on an optional predicate. + * @brief Selects one of two optional values based on a predicate. * * @tparam T Selected value type. * @param out Destination optional selected value. * @param true_value Optional value selected when @p pred is true. * @param false_value Optional value selected when @p pred is false. - * @param pred Optional selection predicate. + * @param pred Selection predicate. */ template __device__ void if_else(cuda::std::optional* out, @@ -229,13 +229,7 @@ __device__ void if_else(cuda::std::optional* out, cuda::std::optional const* false_value, cuda::std::optional const* pred) { - if (pred->has_value() && true_value->has_value() && false_value->has_value()) { - T r; - if_else(&r, &true_value->value(), &false_value->value(), &pred->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + *out = pred->value_or(false) ? *true_value : *false_value; } } // namespace ops From 85bdb0aac898843c1e94b08369908fe1263b9c7b Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Fri, 22 May 2026 09:10:13 +0000 Subject: [PATCH 21/34] doc: clarify if_else parameter description for false_value --- cpp/include/cudf/operators/logic.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cudf/operators/logic.cuh b/cpp/include/cudf/operators/logic.cuh index 97b9c61a5e8e..174201361bed 100644 --- a/cpp/include/cudf/operators/logic.cuh +++ b/cpp/include/cudf/operators/logic.cuh @@ -220,7 +220,7 @@ __device__ void if_else(T* out, T const* true_value, T const* false_value, bool * @tparam T Selected value type. * @param out Destination optional selected value. * @param true_value Optional value selected when @p pred is true. - * @param false_value Optional value selected when @p pred is false. + * @param false_value Optional value selected when @p pred is false or null. * @param pred Selection predicate. */ template From 0dc859e6446ba0b3ddb9ccfdbfeea26cb391944e Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 28 May 2026 11:41:50 +0000 Subject: [PATCH 22/34] refactoring + doc cleanup --- .../cudf/operators/ansi_arithmetic.cuh | 265 +++++++-------- cpp/include/cudf/operators/arithmetic.cuh | 230 ++++++------- cpp/include/cudf/operators/bitwise.cuh | 96 +++--- cpp/include/cudf/operators/casts.cuh | 157 +++++---- cpp/include/cudf/operators/comparison.cuh | 112 +++---- cpp/include/cudf/operators/identity.cuh | 2 +- cpp/include/cudf/operators/logic.cuh | 82 ++--- cpp/include/cudf/operators/math.cuh | 225 ++++++------- cpp/include/cudf/operators/null_handling.cuh | 36 +-- cpp/include/cudf/operators/trigonometric.cuh | 301 ++++++++---------- 10 files changed, 701 insertions(+), 805 deletions(-) diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index 3b882bae6726..cfcbafa6316d 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -18,13 +18,13 @@ namespace CUDF_EXPORT cudf { namespace ops { /** - * @brief Adds integral operands with overflow detection. + * @brief Adds operands with overflow detection. * - * @tparam T Integral type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. - * @return errc::OVERFLOW on overflow, else errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_add(T* out, T const* a, T const* b) @@ -37,10 +37,10 @@ __device__ errc ansi_add(T* out, T const* a, T const* b) } /** - * @brief Adds floating-point operands. + * @brief Adds operands with overflow detection. * - * @tparam T Floating-point type. - * @return errc::SUCCESS. + * @tparam T Value type. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_add(T* out, T const* a, T const* b) @@ -51,10 +51,10 @@ __device__ errc ansi_add(T* out, T const* a, T const* b) } /** - * @brief Adds fixed-point decimal operands with overflow detection. + * @brief Adds operands with overflow detection. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on overflow, else errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_add(numeric::decimal* out, @@ -73,10 +73,10 @@ __device__ errc ansi_add(numeric::decimal* out, } /** - * @brief Adds optional operands with ANSI overflow behavior. + * @brief Adds operands with overflow detection. * - * @tparam T Operand and result type. - * @return Operation status from underlying add, or errc::SUCCESS. + * @tparam T Value type. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_add(cuda::std::optional* out, @@ -98,13 +98,13 @@ __device__ errc ansi_add(cuda::std::optional* out, } /** - * @brief Subtracts integral operands with overflow detection. + * @brief Subtracts operands with overflow detection. * - * @tparam T Integral type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Minuend. * @param b Subtrahend. - * @return errc::OVERFLOW on overflow, else errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_sub(T* out, T const* a, T const* b) @@ -117,10 +117,10 @@ __device__ errc ansi_sub(T* out, T const* a, T const* b) } /** - * @brief Subtracts floating-point operands. + * @brief Subtracts operands with overflow detection. * - * @tparam T Floating-point type. - * @return errc::SUCCESS. + * @tparam T Value type. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_sub(T* out, T const* a, T const* b) @@ -131,10 +131,10 @@ __device__ errc ansi_sub(T* out, T const* a, T const* b) } /** - * @brief Subtracts fixed-point decimal operands with overflow detection. + * @brief Subtracts operands with overflow detection. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on overflow, else errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_sub(numeric::decimal* out, @@ -153,10 +153,10 @@ __device__ errc ansi_sub(numeric::decimal* out, } /** - * @brief Subtracts optional operands with ANSI overflow behavior. + * @brief Subtracts operands with overflow detection. * - * @tparam T Operand and result type. - * @return Operation status from underlying subtract, or errc::SUCCESS. + * @tparam T Value type. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_sub(cuda::std::optional* out, @@ -177,13 +177,13 @@ __device__ errc ansi_sub(cuda::std::optional* out, } /** - * @brief Multiplies integral operands with overflow detection. + * @brief Multiplies operands with overflow detection. * - * @tparam T Integral type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. - * @return errc::OVERFLOW on overflow, else errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_mul(T* out, T const* a, T const* b) @@ -196,10 +196,10 @@ __device__ errc ansi_mul(T* out, T const* a, T const* b) } /** - * @brief Multiplies floating-point operands. + * @brief Multiplies operands with overflow detection. * - * @tparam T Floating-point type. - * @return errc::SUCCESS. + * @tparam T Value type. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_mul(T* out, T const* a, T const* b) @@ -210,10 +210,10 @@ __device__ errc ansi_mul(T* out, T const* a, T const* b) } /** - * @brief Multiplies fixed-point decimal operands with overflow detection. + * @brief Multiplies operands with overflow detection. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on overflow, else errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_mul(numeric::decimal* out, @@ -228,10 +228,10 @@ __device__ errc ansi_mul(numeric::decimal* out, } /** - * @brief Multiplies optional operands with ANSI overflow behavior. + * @brief Multiplies optional operands with overflow detection. * - * @tparam T Operand and result type. - * @return Operation status from underlying multiply, or errc::SUCCESS. + * @tparam T Value type. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_mul(cuda::std::optional* out, @@ -252,13 +252,14 @@ __device__ errc ansi_mul(cuda::std::optional* out, } /** - * @brief Divides integral operands with divide-by-zero checks. + * @brief Divides operands with ANSI checks. * - * @tparam T Integral type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. - * @return errc::DIVISION_BY_ZERO on zero divisor, errc::OVERFLOW on overflow, else errc::SUCCESS. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, `errc::OVERFLOW` on overflow, else + * `errc::SUCCESS`. */ template __device__ errc ansi_div(T* out, T const* a, T const* b) @@ -272,7 +273,7 @@ __device__ errc ansi_div(T* out, T const* a, T const* b) } /** - * @brief Divides floating-point operands. + * @brief Divides operands with ANSI checks. * * @tparam T Floating-point type. * @return errc::SUCCESS. @@ -286,10 +287,11 @@ __device__ errc ansi_div(T* out, T const* a, T const* b) } /** - * @brief Divides fixed-point decimal operands with ANSI checks. + * @brief Divides operands with ANSI checks. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on overflow or zero divisor, else errc::SUCCESS. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, `errc::OVERFLOW` on overflow, else + * `errc::SUCCESS`. */ template __device__ errc ansi_div(numeric::decimal* out, @@ -306,10 +308,11 @@ __device__ errc ansi_div(numeric::decimal* out, } /** - * @brief Divides optional operands with ANSI overflow behavior. + * @brief Divides operands with ANSI checks. * - * @tparam T Operand and result type. - * @return Operation status from underlying divide, or errc::SUCCESS. + * @tparam T Value type. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, `errc::OVERFLOW` on overflow, else + * `errc::SUCCESS`. */ template __device__ errc ansi_div(cuda::std::optional* out, @@ -330,13 +333,13 @@ __device__ errc ansi_div(cuda::std::optional* out, } /** - * @brief Computes signed integral modulus with ANSI checks. + * @brief Computes modulus with ANSI checks. * - * @tparam T Signed integral type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. - * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::SUCCESS. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. */ template __device__ errc ansi_mod(T* out, T const* a, T const* b) @@ -355,10 +358,10 @@ __device__ errc ansi_mod(T* out, T const* a, T const* b) } /** - * @brief Computes unsigned integral modulus with ANSI checks. + * @brief Computes modulus with ANSI checks. * - * @tparam T Unsigned integral type. - * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::SUCCESS. + * @tparam T Value type. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. */ template __device__ errc ansi_mod(T* out, T const* a, T const* b) @@ -370,9 +373,9 @@ __device__ errc ansi_mod(T* out, T const* a, T const* b) } /** - * @brief Computes floating-point modulus for float operands with ANSI checks. + * @brief Computes modulus with ANSI checks. * - * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::SUCCESS. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. */ __device__ errc ansi_mod(float* out, float const* a, float const* b) { @@ -382,9 +385,9 @@ __device__ errc ansi_mod(float* out, float const* a, float const* b) } /** - * @brief Computes floating-point modulus for double operands with ANSI checks. + * @brief Computes modulus with ANSI checks. * - * @return errc::DIVISION_BY_ZERO on zero divisor, else errc::SUCCESS. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. */ __device__ errc ansi_mod(double* out, double const* a, double const* b) { @@ -394,10 +397,10 @@ __device__ errc ansi_mod(double* out, double const* a, double const* b) } /** - * @brief Computes fixed-point decimal modulus with ANSI checks. + * @brief Computes modulus with ANSI checks. * * @tparam R Decimal representation type. - * @return errc::DIVISION_BY_ZERO or propagated status, else errc::SUCCESS. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. */ template __device__ errc ansi_mod(numeric::decimal* out, @@ -415,10 +418,10 @@ __device__ errc ansi_mod(numeric::decimal* out, } /** - * @brief Computes modulus for optional operands with ANSI behavior. + * @brief Computes modulus with ANSI checks. * - * @tparam T Operand and result type. - * @return Operation status from underlying modulus, or errc::SUCCESS. + * @tparam T Value type. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. */ template __device__ errc ansi_mod(cuda::std::optional* out, @@ -439,12 +442,12 @@ __device__ errc ansi_mod(cuda::std::optional* out, } /** - * @brief Computes absolute value for signed integral inputs with ANSI overflow checks. + * @brief Computes absolute value with ANSI overflow checks. * - * @tparam T Signed integral type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. - * @return errc::OVERFLOW on minimum representable input, else errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_abs(T* out, T const* a) @@ -456,10 +459,10 @@ __device__ errc ansi_abs(T* out, T const* a) } /** - * @brief Returns unsigned input unchanged for ANSI absolute value. + * @brief Computes absolute value with ANSI overflow checks. * - * @tparam T Unsigned integral type. - * @return errc::SUCCESS. + * @tparam T Value type. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_abs(T* out, T const* a) @@ -470,24 +473,24 @@ __device__ errc ansi_abs(T* out, T const* a) } /** - * @brief Computes absolute value for floating-point inputs. + * @brief Computes absolute value with ANSI overflow checks. * - * @tparam T Floating-point type. - * @return errc::SUCCESS. + * @tparam T Value type. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template -__device__ errc ansi_abs(T* out, T const* a) requires(cuda::std::is_floating_point_v) +__device__ errc ansi_abs(T* out, T const* a) { - *out = (*a < 0) ? -(*a) : *a; + *out = cuda::std::fabs(*a); return errc::SUCCESS; } /** - * @brief Computes absolute value for decimal inputs with ANSI overflow checks. + * @brief Computes absolute value with ANSI overflow checks. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on minimum representable input, else errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else errc::SUCCESS. */ template __device__ errc ansi_abs(numeric::decimal* out, numeric::decimal const* a) @@ -499,10 +502,10 @@ __device__ errc ansi_abs(numeric::decimal* out, numeric::decimal const* a) } /** - * @brief Computes absolute value for optional inputs with ANSI overflow checks. + * @brief Computes absolute value for with ANSI overflow checks. * * @tparam T Value type. - * @return Operation status from underlying abs, or errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_abs(cuda::std::optional* out, cuda::std::optional const* a) @@ -521,12 +524,12 @@ __device__ errc ansi_abs(cuda::std::optional* out, cuda::std::optional con } /** - * @brief Computes unary negation for signed inputs with ANSI overflow checks. + * @brief Computes unary negation with ANSI overflow checks. * - * @tparam T Signed type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. - * @return errc::OVERFLOW on minimum representable input, else errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_neg(T* out, T const* a) @@ -538,10 +541,10 @@ __device__ errc ansi_neg(T* out, T const* a) } /** - * @brief Computes unary negation for decimal inputs with ANSI overflow checks. + * @brief Computes unary negation with ANSI overflow checks. * * @tparam R Decimal representation type. - * @return errc::OVERFLOW on minimum representable input, else errc::SUCCESS. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_neg(numeric::decimal* out, numeric::decimal const* a) @@ -553,10 +556,10 @@ __device__ errc ansi_neg(numeric::decimal* out, numeric::decimal const* a) } /** - * @brief Computes unary negation for optional inputs with ANSI checks. + * @brief Computes unary negation with ANSI overflow checks. * - * @tparam T Signed type. - * @return Operation status from underlying negate, or errc::SUCCESS. + * @tparam T Value type. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. */ template __device__ errc ansi_neg(cuda::std::optional* out, cuda::std::optional const* a) @@ -578,10 +581,10 @@ __device__ errc ansi_neg(cuda::std::optional* out, cuda::std::optional con * @brief Validates decimal precision against a target precision value. * * @tparam R Decimal representation type. - * @param out Destination decimal value. + * @param out Result destination. * @param a Input decimal value. * @param precision Maximum allowed precision. - * @return errc::OVERFLOW when precision is invalid or exceeded, else errc::SUCCESS. + * @return `errc::OVERFLOW` when precision is invalid or exceeded, else `errc::SUCCESS`. */ template __device__ errc ansi_precision_check(numeric::decimal* out, @@ -604,13 +607,13 @@ __device__ errc ansi_precision_check(numeric::decimal* out, } /** - * @brief Validates optional decimal precision against a precision value. + * @brief Validates decimal precision against a target precision value. * - * @tparam T Decimal value type. - * @param out Destination optional value. - * @param a Optional decimal input. + * @tparam T Value type. + * @param out Result destination. + * @param a Decimal input. * @param precision Precision. - * @return Operation status from underlying precision check, or errc::SUCCESS. + * @return `errc::OVERFLOW` when precision is invalid or exceeded, else `errc::SUCCESS`. */ template __device__ errc ansi_precision_check(cuda::std::optional* out, @@ -633,12 +636,12 @@ __device__ errc ansi_precision_check(cuda::std::optional* out, } /** - * @brief ANSI add that returns null instead of propagating arithmetic errors. + * @brief ANSI add that returns null instead of propagating errors. * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void ansi_try_add(cuda::std::optional* out, @@ -658,12 +661,12 @@ __device__ void ansi_try_add(cuda::std::optional* out, } /** - * @brief ANSI subtract that returns null instead of propagating arithmetic errors. + * @brief ANSI subtract that returns null instead of propagating errors. * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional minuend. - * @param b Optional subtrahend. + * @tparam T Value type. + * @param out Result destination. + * @param a Minuend. + * @param b Subtrahend. */ template __device__ void ansi_try_sub(cuda::std::optional* out, @@ -683,12 +686,12 @@ __device__ void ansi_try_sub(cuda::std::optional* out, } /** - * @brief ANSI multiply that returns null instead of propagating arithmetic errors. + * @brief ANSI multiply that returns null instead of propagating errors. * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void ansi_try_mul(cuda::std::optional* out, @@ -708,12 +711,12 @@ __device__ void ansi_try_mul(cuda::std::optional* out, } /** - * @brief ANSI divide that returns null instead of propagating arithmetic errors. + * @brief ANSI divide that returns null instead of propagating errors. * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional dividend. - * @param b Optional divisor. + * @tparam T Value type. + * @param out Result destination. + * @param a Dividend. + * @param b Divisor. */ template __device__ void ansi_try_div(cuda::std::optional* out, @@ -733,12 +736,12 @@ __device__ void ansi_try_div(cuda::std::optional* out, } /** - * @brief ANSI modulus that returns null instead of propagating arithmetic errors. + * @brief ANSI modulus that returns null instead of propagating errors. * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional dividend. - * @param b Optional divisor. + * @tparam T Value type. + * @param out Result destination. + * @param a Dividend. + * @param b Divisor. */ template __device__ void ansi_try_mod(cuda::std::optional* out, @@ -758,11 +761,11 @@ __device__ void ansi_try_mod(cuda::std::optional* out, } /** - * @brief ANSI absolute value that returns null instead of propagating overflow errors. + * @brief ANSI absolute value that returns null instead of propagating errors. * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional input value. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. */ template __device__ void ansi_try_abs(cuda::std::optional* out, cuda::std::optional const* a) @@ -780,11 +783,11 @@ __device__ void ansi_try_abs(cuda::std::optional* out, cuda::std::optional } /** - * @brief ANSI unary negation that returns null instead of propagating overflow errors. + * @brief ANSI unary negation that returns null instead of propagating errors. * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional input value. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. */ template __device__ void ansi_try_neg(cuda::std::optional* out, cuda::std::optional const* a) @@ -802,12 +805,12 @@ __device__ void ansi_try_neg(cuda::std::optional* out, cuda::std::optional } /** - * @brief ANSI precision check that returns null instead of propagating precision errors. + * @brief ANSI precision check that returns null instead of propagating errors. * * @tparam R Decimal representation type. - * @param out Destination optional decimal value. - * @param a Optional decimal input. - * @param precision Optional precision. + * @param out Result destination. + * @param a Decimal input. + * @param precision Precision. */ template __device__ void ansi_try_precision_check(cuda::std::optional>* out, diff --git a/cpp/include/cudf/operators/arithmetic.cuh b/cpp/include/cudf/operators/arithmetic.cuh index f47f36d7dbcf..683302e55f9b 100644 --- a/cpp/include/cudf/operators/arithmetic.cuh +++ b/cpp/include/cudf/operators/arithmetic.cuh @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -15,33 +16,21 @@ namespace CUDF_EXPORT cudf { namespace ops { /** - * @brief Computes absolute value for signed and floating-point inputs. + * @brief Computes absolute value. * - * @tparam T Input and output type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ template __device__ void abs(T* out, T const* a) - requires(cuda::std::is_signed_v || cuda::std::is_floating_point_v) + requires(cuda::std::is_integral_v || cuda::std::is_floating_point_v) { - *out = (*a < 0) ? -*a : *a; + *out = cuda::std::abs(*a); } /** - * @brief Returns unsigned input unchanged for absolute value. - * - * @tparam T Unsigned input and output type. - */ -template -__device__ void abs(T* out, T const* a) - requires(cuda::std::is_unsigned_v) -{ - *out = *a; -} - -/** - * @brief Computes absolute value for fixed-point decimal values. + * @brief Computes absolute value. * * @tparam R Decimal representation type. */ @@ -53,11 +42,11 @@ __device__ void abs(numeric::decimal* out, numeric::decimal const* a) } /** - * @brief Computes absolute value for optional input. + * @brief Computes absolute value. * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. */ template __device__ void abs(cuda::std::optional* out, cuda::std::optional const* a) @@ -74,8 +63,8 @@ __device__ void abs(cuda::std::optional* out, cuda::std::optional const* a /** * @brief Computes sum of two values. * - * @tparam T Operand and result type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -86,10 +75,10 @@ __device__ void add(T* out, T const* a, T const* b) } /** - * @brief Computes sum for optional operands. + * @brief Computes sum of two values. * - * @tparam T Operand and result type. - * @param out Destination optional value. + * @tparam T Value type. + * @param out Result destination. * @param a Left optional operand. * @param b Right optional operand. */ @@ -110,8 +99,8 @@ __device__ void add(cuda::std::optional* out, /** * @brief Computes quotient of two values. * - * @tparam T Operand and result type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ @@ -122,12 +111,12 @@ __device__ void div(T* out, T const* a, T const* b) } /** - * @brief Computes quotient for optional operands. + * @brief Computes quotient of two values. * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional dividend. - * @param b Optional divisor. + * @tparam T Value type. + * @param out Result destination. + * @param a Dividend. + * @param b Divisor. */ template __device__ void div(cuda::std::optional* out, @@ -144,10 +133,10 @@ __device__ void div(cuda::std::optional* out, } /** - * @brief Computes floor division for integral operands. + * @brief Computes floor division of two values. * - * @tparam T Integral operand and result type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ @@ -159,37 +148,28 @@ __device__ void floor_div(T* out, T const* a, T const* b) } /** - * @brief Computes floor division for float operands. + * @brief Computes floor division of two values. * - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ -__device__ inline void floor_div(float* out, float const* a, float const* b) +template +__device__ inline void floor_div(T* out, T const* a, T const* b) + requires(cuda::std::is_floating_point_v) { - *out = ::floorf(*a / *b); + *out = cuda::std::floor(*a / *b); } /** - * @brief Computes floor division for double operands. + * @brief Computes floor division of two values. * - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ -__device__ inline void floor_div(double* out, double const* a, double const* b) -{ - *out = ::floor(*a / *b); -} - -/** - * @brief Computes floor division for optional operands. - * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional dividend. - * @param b Optional divisor. - */ template __device__ void floor_div(cuda::std::optional* out, cuda::std::optional const* a, @@ -205,45 +185,43 @@ __device__ void floor_div(cuda::std::optional* out, } /** - * @brief Computes remainder of integer division. + * @brief Computes remainder of two values. * - * @tparam T Operand and result type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ template __device__ void mod(T* out, T const* a, T const* b) + requires(cuda::std::is_integral_v || cudf::is_fixed_point) { *out = (*a % *b); } /** - * @brief Computes floating-point remainder for float operands. + * @brief Computes remainder of two values. * - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ -__device__ inline void mod(float* out, float const* a, float const* b) { *out = ::fmodf(*a, *b); } +template +__device__ inline void mod(T* out, T const* a, T const* b) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::fmod(*a, *b); +} /** - * @brief Computes floating-point remainder for double operands. + * @brief Computes remainder of two values. * - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ -__device__ inline void mod(double* out, double const* a, double const* b) { *out = ::fmod(*a, *b); } - -/** - * @brief Computes remainder for optional operands. - * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional dividend. - * @param b Optional divisor. - */ template __device__ void mod(cuda::std::optional* out, cuda::std::optional const* a, @@ -261,49 +239,41 @@ __device__ void mod(cuda::std::optional* out, /** * @brief Computes Python-style modulus. * - * @tparam T Integral operand and result type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ template __device__ void pymod(T* out, T const* a, T const* b) + requires(cuda::std::is_integral_v || cudf::is_fixed_point) { *out = (*a % *b + *b) % *b; } /** - * @brief Computes Python-style modulus for float operands. + * @brief Computes Python-style modulus. * - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ -__device__ inline void pymod(float* out, float const* a, float const* b) +template +__device__ inline void pymod(T* out, T const* a, T const* b) + requires(cuda::std::is_floating_point_v) { - *out = ::fmodf(::fmodf(*a, *b) + *b, *b); + *out = cuda::std::fmod(cuda::std::fmod(*a, *b) + *b, *b); } /** - * @brief Computes Python-style modulus for double operands. + * @brief Computes Python-style modulus. * - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ -__device__ inline void pymod(double* out, double const* a, double const* b) -{ - *out = ::fmod(::fmod(*a, *b) + *b, *b); -} - -/** - * @brief Computes Python-style modulus for optional operands. - * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional dividend. - * @param b Optional divisor. - */ template __device__ void pymod(cuda::std::optional* out, cuda::std::optional const* a, @@ -321,8 +291,8 @@ __device__ void pymod(cuda::std::optional* out, /** * @brief Computes product of two values. * - * @tparam T Operand and result type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -333,12 +303,12 @@ __device__ void mul(T* out, T const* a, T const* b) } /** - * @brief Computes product for optional operands. + * @brief Computes product of two values. * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void mul(cuda::std::optional* out, @@ -355,10 +325,10 @@ __device__ void mul(cuda::std::optional* out, } /** - * @brief Computes unary negation for signed inputs. + * @brief Computes unary negation. * - * @tparam T Signed input and output type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ template @@ -369,25 +339,25 @@ __device__ void neg(T* out, T const* a) } /** - * @brief Computes unary negation for decimal inputs. + * @brief Computes unary negation. * - * @tparam Rep Decimal representation type. - * @param out Destination decimal value. - * @param a Input decimal value. + * @tparam R Decimal representation type. + * @param out Result destination. + * @param a Input value. */ -template -__device__ void neg(numeric::decimal* out, numeric::decimal const* a) +template +__device__ void neg(numeric::decimal* out, numeric::decimal const* a) { auto rep = -a->value(); - *out = numeric::decimal{numeric::scaled_integer{rep, a->scale()}}; + *out = numeric::decimal{numeric::scaled_integer{rep, a->scale()}}; } /** - * @brief Computes unary negation for optional input. + * @brief Computes unary negation. * - * @tparam T Signed input and output type. - * @param out Destination optional value. - * @param a Optional input value. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. */ template __device__ void neg(cuda::std::optional* out, cuda::std::optional const* a) @@ -404,8 +374,8 @@ __device__ void neg(cuda::std::optional* out, cuda::std::optional const* a /** * @brief Computes subtraction of two values. * - * @tparam T Operand and result type. - * @param out Destination value. + * @tparam T Value type. + * @param out Result destination. * @param a Minuend. * @param b Subtrahend. */ @@ -416,12 +386,12 @@ __device__ void sub(T* out, T const* a, T const* b) } /** - * @brief Computes subtraction for optional operands. + * @brief Computes subtraction of two values. * - * @tparam T Operand and result type. - * @param out Destination optional value. - * @param a Optional minuend. - * @param b Optional subtrahend. + * @tparam T Value type. + * @param out Result destination. + * @param a Minuend. + * @param b Subtrahend. */ template __device__ void sub(cuda::std::optional* out, @@ -440,8 +410,8 @@ __device__ void sub(cuda::std::optional* out, /** * @brief Computes true division and returns a double. * - * @tparam T Input operand type. - * @param out Destination double value. + * @tparam T Value type. + * @param out Result destination. * @param a Dividend. * @param b Divisor. */ @@ -453,12 +423,12 @@ __device__ void true_div(double* out, T const* a, T const* b) } /** - * @brief Computes true division for optional operands and returns optional double. + * @brief Computes true division and returns a double. * - * @tparam T Input operand type. - * @param out Destination optional double value. - * @param a Optional dividend. - * @param b Optional divisor. + * @tparam T Value type. + * @param out Result destination. + * @param a Dividend. + * @param b Divisor. */ template __device__ void true_div(cuda::std::optional* out, diff --git a/cpp/include/cudf/operators/bitwise.cuh b/cpp/include/cudf/operators/bitwise.cuh index 18612ee8cb8e..c6b4b5d2bbdb 100644 --- a/cpp/include/cudf/operators/bitwise.cuh +++ b/cpp/include/cudf/operators/bitwise.cuh @@ -14,10 +14,10 @@ namespace ops { /** * @brief Computes bitwise AND of two values. * - * @tparam T Operand and result type. - * @param out Destination for the computed value. - * @param a Left input operand. - * @param b Right input operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void bit_and(T* out, T const* a, T const* b) @@ -26,12 +26,12 @@ __device__ void bit_and(T* out, T const* a, T const* b) } /** - * @brief Computes bitwise AND for optional operands. + * @brief Computes bitwise AND of two values. * - * @tparam T Operand and result type. - * @param out Destination optional result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void bit_and(cuda::std::optional* out, @@ -50,9 +50,9 @@ __device__ void bit_and(cuda::std::optional* out, /** * @brief Computes bitwise NOT of one value. * - * @tparam T Operand and result type. - * @param out Destination for the computed value. - * @param a Input operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. */ template __device__ void bit_invert(T* out, T const* a) @@ -61,11 +61,11 @@ __device__ void bit_invert(T* out, T const* a) } /** - * @brief Computes bitwise NOT for an optional operand. + * @brief Computes bitwise NOT of one value. * - * @tparam T Operand and result type. - * @param out Destination optional result. - * @param a Optional input operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. */ template __device__ void bit_invert(cuda::std::optional* out, cuda::std::optional const* a) @@ -82,10 +82,10 @@ __device__ void bit_invert(cuda::std::optional* out, cuda::std::optional c /** * @brief Computes bitwise OR of two values. * - * @tparam T Operand and result type. - * @param out Destination for the computed value. - * @param a Left input operand. - * @param b Right input operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void bit_or(T* out, T const* a, T const* b) @@ -94,12 +94,12 @@ __device__ void bit_or(T* out, T const* a, T const* b) } /** - * @brief Computes bitwise OR for optional operands. + * @brief Computes bitwise OR of two values. * - * @tparam T Operand and result type. - * @param out Destination optional result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void bit_or(cuda::std::optional* out, @@ -118,10 +118,10 @@ __device__ void bit_or(cuda::std::optional* out, /** * @brief Computes bitwise XOR of two values. * - * @tparam T Operand and result type. - * @param out Destination for the computed value. - * @param a Left input operand. - * @param b Right input operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void bit_xor(T* out, T const* a, T const* b) @@ -130,12 +130,12 @@ __device__ void bit_xor(T* out, T const* a, T const* b) } /** - * @brief Computes bitwise XOR for optional operands. + * @brief Computes bitwise XOR of two values. * - * @tparam T Operand and result type. - * @param out Destination optional result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void bit_xor(cuda::std::optional* out, @@ -154,8 +154,8 @@ __device__ void bit_xor(cuda::std::optional* out, /** * @brief Shifts a value left by a bit count. * - * @tparam T Operand and result type. - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. * @param b Shift count. */ @@ -166,12 +166,12 @@ __device__ void bit_shift_left(T* out, T const* a, T const* b) } /** - * @brief Shifts an optional value left by an optional bit count. + * @brief Shifts a value left by a bit count. * - * @tparam T Operand and result type. - * @param out Destination optional result. - * @param a Optional input value. - * @param b Optional shift count. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. + * @param b Shift count. */ template __device__ void bit_shift_left(cuda::std::optional* out, @@ -190,8 +190,8 @@ __device__ void bit_shift_left(cuda::std::optional* out, /** * @brief Shifts a value right by a bit count. * - * @tparam T Operand and result type. - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. * @param b Shift count. */ @@ -202,12 +202,12 @@ __device__ void bit_shift_right(T* out, T const* a, T const* b) } /** - * @brief Shifts an optional value right by an optional bit count. + * @brief Shifts a value right by a bit count. * - * @tparam T Operand and result type. - * @param out Destination optional result. - * @param a Optional input value. - * @param b Optional shift count. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. + * @param b Shift count. */ template __device__ void bit_shift_right(cuda::std::optional* out, diff --git a/cpp/include/cudf/operators/casts.cuh b/cpp/include/cudf/operators/casts.cuh index cf9767b86bf8..9dac2e488094 100644 --- a/cpp/include/cudf/operators/casts.cuh +++ b/cpp/include/cudf/operators/casts.cuh @@ -17,9 +17,8 @@ namespace ops { /** * @brief Casts input values to bool. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam T Source type. - * @param out Destination cast value. + * @param out Result destination. * @param a Input value. */ template @@ -29,11 +28,11 @@ __device__ void cast_to_b8(bool* out, T const* a) } /** - * @brief Casts optional input values to optional bool. + * @brief Casts input values to bool. * * @tparam T Source type. - * @param out Destination optional cast value. - * @param a Optional input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_b8(cuda::std::optional* out, cuda::std::optional const* a) @@ -50,9 +49,8 @@ __device__ void cast_to_b8(cuda::std::optional* out, cuda::std::optional @@ -62,11 +60,11 @@ __device__ void cast_to_i8(int8_t* out, T const* a) } /** - * @brief Casts optional input values to optional int8_t. + * @brief Casts input values to int8_t. * * @tparam T Source type. - * @param out Destination optional cast value. - * @param a Optional input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_i8(cuda::std::optional* out, cuda::std::optional const* a) @@ -83,9 +81,8 @@ __device__ void cast_to_i8(cuda::std::optional* out, cuda::std::optional /** * @brief Casts input values to int16_t. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam T Source type. - * @param out Destination cast value. + * @param out Result destination. * @param a Input value. */ template @@ -95,11 +92,11 @@ __device__ void cast_to_i16(int16_t* out, T const* a) } /** - * @brief Casts optional input values to optional int16_t. + * @brief Casts input values to int16_t. * * @tparam T Source type. - * @param out Destination optional cast value. - * @param a Optional input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_i16(cuda::std::optional* out, cuda::std::optional const* a) @@ -116,9 +113,8 @@ __device__ void cast_to_i16(cuda::std::optional* out, cuda::std::option /** * @brief Casts input values to int32_t. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam T Source type. - * @param out Destination cast value. + * @param out Result destination. * @param a Input value. */ template @@ -128,11 +124,11 @@ __device__ void cast_to_i32(int32_t* out, T const* a) } /** - * @brief Casts optional input values to optional int32_t. + * @brief Casts input values to int32_t. * * @tparam T Source type. - * @param out Destination optional cast value. - * @param a Optional input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_i32(cuda::std::optional* out, cuda::std::optional const* a) @@ -149,9 +145,8 @@ __device__ void cast_to_i32(cuda::std::optional* out, cuda::std::option /** * @brief Casts input values to int64_t. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam T Source type. - * @param out Destination cast value. + * @param out Result destination. * @param a Input value. */ template @@ -161,11 +156,11 @@ __device__ void cast_to_i64(int64_t* out, T const* a) } /** - * @brief Casts optional input values to optional int64_t. + * @brief Casts input values to int64_t. * * @tparam T Source type. - * @param out Destination optional cast value. - * @param a Optional input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_i64(cuda::std::optional* out, cuda::std::optional const* a) @@ -182,9 +177,8 @@ __device__ void cast_to_i64(cuda::std::optional* out, cuda::std::option /** * @brief Casts input values to uint8_t. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam T Source type. - * @param out Destination cast value. + * @param out Result destination. * @param a Input value. */ template @@ -194,11 +188,11 @@ __device__ void cast_to_u8(uint8_t* out, T const* a) } /** - * @brief Casts optional input values to optional uint8_t. + * @brief Casts input values to uint8_t. * * @tparam T Source type. - * @param out Destination optional cast value. - * @param a Optional input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_u8(cuda::std::optional* out, cuda::std::optional const* a) @@ -215,9 +209,8 @@ __device__ void cast_to_u8(cuda::std::optional* out, cuda::std::optiona /** * @brief Casts input values to uint16_t. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam T Source type. - * @param out Destination cast value. + * @param out Result destination. * @param a Input value. */ template @@ -227,11 +220,11 @@ __device__ void cast_to_u16(uint16_t* out, T const* a) } /** - * @brief Casts optional input values to optional uint16_t. + * @brief Casts input values to uint16_t. * * @tparam T Source type. - * @param out Destination optional cast value. - * @param a Optional input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_u16(cuda::std::optional* out, cuda::std::optional const* a) @@ -248,9 +241,8 @@ __device__ void cast_to_u16(cuda::std::optional* out, cuda::std::optio /** * @brief Casts input values to uint32_t. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam T Source type. - * @param out Destination cast value. + * @param out Result destination. * @param a Input value. */ template @@ -260,11 +252,11 @@ __device__ void cast_to_u32(uint32_t* out, T const* a) } /** - * @brief Casts optional input values to optional uint32_t. + * @brief Casts input values to uint32_t. * * @tparam T Source type. - * @param out Destination optional cast value. - * @param a Optional input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_u32(cuda::std::optional* out, cuda::std::optional const* a) @@ -281,9 +273,8 @@ __device__ void cast_to_u32(cuda::std::optional* out, cuda::std::optio /** * @brief Casts input values to uint64_t. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam T Source type. - * @param out Destination cast value. + * @param out Result destination. * @param a Input value. */ template @@ -293,11 +284,11 @@ __device__ void cast_to_u64(uint64_t* out, T const* a) } /** - * @brief Casts optional input values to optional uint64_t. + * @brief Casts input values to uint64_t. * * @tparam T Source type. - * @param out Destination optional cast value. - * @param a Optional input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_u64(cuda::std::optional* out, cuda::std::optional const* a) @@ -314,8 +305,8 @@ __device__ void cast_to_u64(cuda::std::optional* out, cuda::std::optio /** * @brief Casts input values to float. * - * Overloads support integral, floating-point, fixed-point decimal, and optional inputs. - * @param out Destination cast value. + * @tparam T Source type. + * @param out Result destination. * @param a Input value. */ template @@ -329,7 +320,7 @@ __device__ void cast_to_f32(float* out, T const* a) * @brief Casts fixed-point decimal values to float. * * @tparam R Source decimal representation type. - * @param out Destination cast value. + * @param out Result destination. * @param a Source decimal value. */ template @@ -339,10 +330,10 @@ __device__ void cast_to_f32(float* out, numeric::decimal const* a) } /** - * @brief Casts optional input values to optional float. + * @brief Casts input values to float. * * @tparam T Source type. - * @param out Destination optional cast value. + * @param out Result destination. * @param a Optional input value. */ template @@ -360,8 +351,8 @@ __device__ void cast_to_f32(cuda::std::optional* out, cuda::std::optional /** * @brief Casts input values to double. * - * Overloads support integral, floating-point, fixed-point decimal, and optional inputs. - * @param out Destination cast value. + * @tparam T Source type. + * @param out Result destination. * @param a Input value. */ template @@ -372,11 +363,11 @@ __device__ void cast_to_f64(double* out, T const* a) } /** - * @brief Casts fixed-point decimal values to double. + * @brief Casts input values to double. * * @tparam R Source decimal representation type. - * @param out Destination cast value. - * @param a Source decimal value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_f64(double* out, numeric::decimal const* a) @@ -388,8 +379,8 @@ __device__ void cast_to_f64(double* out, numeric::decimal const* a) * @brief Casts optional input values to optional double. * * @tparam T Source type. - * @param out Destination optional cast value. - * @param a Optional input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_f64(cuda::std::optional* out, cuda::std::optional const* a) @@ -410,8 +401,8 @@ namespace detail { * * @tparam To Destination representation type. * @tparam From Source representation type. - * @param out Destination decimal value. - * @param a Source decimal value. + * @param out Result destination. + * @param a Input value. */ template __device__ void decimal_cast(numeric::decimal* out, numeric::decimal const* a) @@ -422,15 +413,12 @@ __device__ void decimal_cast(numeric::decimal* out, numeric::decimal c } // namespace detail -// TODO(lamarrr): CAST_TO_DEC32 for int & float - /** * @brief Casts decimal input values to decimal32. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam R Source decimal representation type. - * @param out Destination decimal32 value. - * @param a Source decimal value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_dec32(numeric::decimal32* out, numeric::decimal const* a) @@ -439,11 +427,11 @@ __device__ void cast_to_dec32(numeric::decimal32* out, numeric::decimal const } /** - * @brief Casts optional decimal input values to optional decimal32. + * @brief Casts decimal input values to decimal32. * * @tparam R Source decimal representation type. - * @param out Destination optional decimal32 value. - * @param a Optional decimal input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_dec32(cuda::std::optional* out, @@ -461,10 +449,9 @@ __device__ void cast_to_dec32(cuda::std::optional* out, /** * @brief Casts decimal input values to decimal64. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam R Source decimal representation type. - * @param out Destination decimal64 value. - * @param a Source decimal value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_dec64(numeric::decimal64* out, numeric::decimal const* a) @@ -476,8 +463,8 @@ __device__ void cast_to_dec64(numeric::decimal64* out, numeric::decimal const * @brief Casts optional decimal input values to optional decimal64. * * @tparam R Source decimal representation type. - * @param out Destination optional decimal64 value. - * @param a Optional decimal input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_dec64(cuda::std::optional* out, @@ -495,10 +482,9 @@ __device__ void cast_to_dec64(cuda::std::optional* out, /** * @brief Casts decimal input values to decimal128. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam R Source decimal representation type. - * @param out Destination decimal128 value. - * @param a Source decimal value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_dec128(numeric::decimal128* out, numeric::decimal const* a) @@ -507,11 +493,11 @@ __device__ void cast_to_dec128(numeric::decimal128* out, numeric::decimal con } /** - * @brief Casts optional decimal input values to optional decimal128. + * @brief Casts decimal input values to decimal128. * * @tparam R Source decimal representation type. - * @param out Destination optional decimal128 value. - * @param a Optional decimal input value. + * @param out Result destination. + * @param a Input value. */ template __device__ void cast_to_dec128(cuda::std::optional* out, @@ -527,12 +513,11 @@ __device__ void cast_to_dec128(cuda::std::optional* out, } /** - * @brief Rescales fixed-point decimal values to a target scale. + * @brief Rescales decimal input values to a target scale. * - * Scalar and optional overloads are provided; optional overloads propagate nulls. * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Source decimal value. + * @param out Result destination. + * @param a Input value. * @param new_scale Target decimal scale. */ template @@ -544,12 +529,12 @@ __device__ void rescale(numeric::decimal* out, } /** - * @brief Rescales optional fixed-point decimal input values. + * @brief Rescales decimal input values to a target scale. * * @tparam R Decimal representation type. - * @param out Destination optional decimal value. - * @param a Optional source decimal value. - * @param new_scale Optional target decimal scale. + * @param out Result destination. + * @param a Input value. + * @param new_scale Target decimal scale. */ template __device__ void rescale(cuda::std::optional>* out, diff --git a/cpp/include/cudf/operators/comparison.cuh b/cpp/include/cudf/operators/comparison.cuh index f5a823affa2e..432cfed72d4f 100644 --- a/cpp/include/cudf/operators/comparison.cuh +++ b/cpp/include/cudf/operators/comparison.cuh @@ -12,10 +12,10 @@ namespace CUDF_EXPORT cudf { namespace ops { /** - * @brief Tests equality between two values. + * @brief Tests `a == b`. * - * @tparam T Operand type. - * @param out Destination for the comparison result. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -26,12 +26,12 @@ __device__ void equal(bool* out, T const* a, T const* b) } /** - * @brief Tests equality between optional operands. + * @brief Tests `a == b`. * - * @tparam T Operand type. - * @param out Destination optional boolean result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void equal(cuda::std::optional* out, @@ -50,10 +50,10 @@ __device__ void equal(cuda::std::optional* out, } /** - * @brief Tests inequality between two values. + * @brief Tests `a != b`. * - * @tparam T Operand type. - * @param out Destination for the comparison result. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -64,12 +64,12 @@ __device__ void not_equal(bool* out, T const* a, T const* b) } /** - * @brief Tests inequality between optional operands. + * @brief Tests `a != b`. * - * @tparam T Operand type. - * @param out Destination optional boolean result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void not_equal(cuda::std::optional* out, @@ -88,10 +88,10 @@ __device__ void not_equal(cuda::std::optional* out, } /** - * @brief Tests whether the left operand is greater than the right operand. + * @brief Tests `a > b`. * - * @tparam T Operand type. - * @param out Destination for the comparison result. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -102,12 +102,12 @@ __device__ void greater(bool* out, T const* a, T const* b) } /** - * @brief Tests whether one optional operand is greater than another. + * @brief Tests `a > b`. * - * @tparam T Operand type. - * @param out Destination optional boolean result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void greater(cuda::std::optional* out, @@ -124,10 +124,10 @@ __device__ void greater(cuda::std::optional* out, } /** - * @brief Tests whether the left operand is greater than or equal to the right operand. + * @brief Tests `a >= b`. * - * @tparam T Operand type. - * @param out Destination for the comparison result. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -138,12 +138,12 @@ __device__ void greater_equal(bool* out, T const* a, T const* b) } /** - * @brief Tests whether one optional operand is greater than or equal to another. + * @brief Tests `a >= b`. * - * @tparam T Operand type. - * @param out Destination optional boolean result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void greater_equal(cuda::std::optional* out, @@ -160,10 +160,10 @@ __device__ void greater_equal(cuda::std::optional* out, } /** - * @brief Tests whether the left operand is less than the right operand. + * @brief Tests `a < b`. * - * @tparam T Operand type. - * @param out Destination for the comparison result. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -174,12 +174,12 @@ __device__ void less(bool* out, T const* a, T const* b) } /** - * @brief Tests whether one optional operand is less than another. + * @brief Tests `a < b`. * - * @tparam T Operand type. - * @param out Destination optional boolean result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void less(cuda::std::optional* out, @@ -196,10 +196,10 @@ __device__ void less(cuda::std::optional* out, } /** - * @brief Tests whether the left operand is less than or equal to the right operand. + * @brief Tests `a <= b`. * - * @tparam T Operand type. - * @param out Destination for the comparison result. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -210,12 +210,12 @@ __device__ void less_equal(bool* out, T const* a, T const* b) } /** - * @brief Tests whether one optional operand is less than or equal to another. + * @brief Tests `a <= b`. * - * @tparam T Operand type. - * @param out Destination optional boolean result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void less_equal(cuda::std::optional* out, @@ -232,10 +232,10 @@ __device__ void less_equal(cuda::std::optional* out, } /** - * @brief Tests equality between two non-optional values for null-aware equality semantics. + * @brief Tests equality between two values for null-aware equality semantics. * - * @tparam T Operand type. - * @param out Destination for the comparison result. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -246,12 +246,12 @@ __device__ void null_equal(bool* out, T const* a, T const* b) } /** - * @brief Tests null-aware equality between optional operands. + * @brief Tests equality between two values for null-aware equality semantics. * - * @tparam T Operand type. - * @param out Destination optional boolean result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void null_equal(cuda::std::optional* out, diff --git a/cpp/include/cudf/operators/identity.cuh b/cpp/include/cudf/operators/identity.cuh index e7cee2521402..67b21eb1c043 100644 --- a/cpp/include/cudf/operators/identity.cuh +++ b/cpp/include/cudf/operators/identity.cuh @@ -15,7 +15,7 @@ namespace ops { * @brief Copies an input value to the output. * * @tparam T Value type. - * @param out Destination value. + * @param out Result destination. * @param a Input value. */ template diff --git a/cpp/include/cudf/operators/logic.cuh b/cpp/include/cudf/operators/logic.cuh index 174201361bed..2ca146e3ab83 100644 --- a/cpp/include/cudf/operators/logic.cuh +++ b/cpp/include/cudf/operators/logic.cuh @@ -12,10 +12,10 @@ namespace CUDF_EXPORT cudf { namespace ops { /** - * @brief Computes logical AND for non-optional operands with null-aware semantics. + * @brief Computes logical AND with null-aware semantics. * * @tparam T Operand type. - * @param out Destination for the logical result. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -26,12 +26,12 @@ __device__ void null_logical_and(bool* out, T const* a, T const* b) } /** - * @brief Computes logical AND for optional operands with three-valued semantics. + * @brief Computes logical AND with null-aware semantics. * - * @tparam T Operand type. - * @param out Destination optional logical result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void null_logical_and(cuda::std::optional* out, @@ -54,10 +54,10 @@ __device__ void null_logical_and(cuda::std::optional* out, } /** - * @brief Computes logical OR for non-optional operands with null-aware semantics. + * @brief Computes logical OR with null-aware semantics. * - * @tparam T Operand type. - * @param out Destination for the logical result. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -68,12 +68,12 @@ __device__ void null_logical_or(bool* out, T const* a, T const* b) } /** - * @brief Computes logical OR for optional operands with three-valued semantics. + * @brief Computes logical OR with null-aware semantics. * - * @tparam T Operand type. - * @param out Destination optional logical result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void null_logical_or(cuda::std::optional* out, @@ -96,10 +96,10 @@ __device__ void null_logical_or(cuda::std::optional* out, } /** - * @brief Computes logical AND for non-optional operands. + * @brief Computes logical AND with null-aware semantics. * * @tparam T Operand type. - * @param out Destination for the logical result. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -110,12 +110,12 @@ __device__ void logical_and(bool* out, T const* a, T const* b) } /** - * @brief Computes logical AND for optional operands. + * @brief Computes logical AND with null-aware semantics. * - * @tparam T Operand type. - * @param out Destination optional logical result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void logical_and(cuda::std::optional* out, @@ -132,10 +132,10 @@ __device__ void logical_and(cuda::std::optional* out, } /** - * @brief Computes logical OR for non-optional operands. + * @brief Computes logical OR with null-aware semantics. * - * @tparam T Operand type. - * @param out Destination for the logical result. + * @tparam T Value type. + * @param out Result destination. * @param a Left operand. * @param b Right operand. */ @@ -146,12 +146,12 @@ __device__ void logical_or(bool* out, T const* a, T const* b) } /** - * @brief Computes logical OR for optional operands. + * @brief Computes logical OR with null-aware semantics. * - * @tparam T Operand type. - * @param out Destination optional logical result. - * @param a Left optional operand. - * @param b Right optional operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Left operand. + * @param b Right operand. */ template __device__ void logical_or(cuda::std::optional* out, @@ -168,10 +168,10 @@ __device__ void logical_or(cuda::std::optional* out, } /** - * @brief Computes logical NOT for a non-optional operand. + * @brief Computes logical NOT with null-aware semantics. * - * @tparam T Operand type. - * @param out Destination for the logical result. + * @tparam T Value type. + * @param out Result destination. * @param a Input operand. */ template @@ -181,11 +181,11 @@ __device__ void logical_not(bool* out, T const* a) } /** - * @brief Computes logical NOT for an optional operand. + * @brief Computes logical NOT with null-aware semantics. * - * @tparam T Operand type. - * @param out Destination optional logical result. - * @param a Optional input operand. + * @tparam T Value type. + * @param out Result destination. + * @param a Input operand. */ template __device__ void logical_not(cuda::std::optional* out, cuda::std::optional const* a) @@ -200,10 +200,10 @@ __device__ void logical_not(cuda::std::optional* out, cuda::std::optional< } /** - * @brief Selects one of two values based on a boolean predicate. + * @brief Selects one of two values based on a predicate. * * @tparam T Selected value type. - * @param out Destination for the selected value. + * @param out Result destination. * @param true_value Value selected when @p pred is true. * @param false_value Value selected when @p pred is false. * @param pred Selection predicate. @@ -215,10 +215,10 @@ __device__ void if_else(T* out, T const* true_value, T const* false_value, bool } /** - * @brief Selects one of two optional values based on a predicate. + * @brief Selects one of two values based on a predicate. * * @tparam T Selected value type. - * @param out Destination optional selected value. + * @param out Result destination. * @param true_value Optional value selected when @p pred is true. * @param false_value Optional value selected when @p pred is false or null. * @param pred Selection predicate. diff --git a/cpp/include/cudf/operators/math.cuh b/cpp/include/cudf/operators/math.cuh index 4aac00e9fe3e..0733e9107094 100644 --- a/cpp/include/cudf/operators/math.cuh +++ b/cpp/include/cudf/operators/math.cuh @@ -7,35 +7,33 @@ #include #include +#include #include namespace CUDF_EXPORT cudf { namespace ops { /** - * @brief Computes cube root. + * @brief Computes cube root * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void cbrt(float* out, float const* a) { *out = ::cbrtf(*a); } +template +__device__ inline void cbrt(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::cbrt(*a); +} /** - * @brief Computes cube root for double input. + * @brief Computes cube root. * - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void cbrt(double* out, double const* a) { *out = ::cbrt(*a); } - -/** - * @brief Computes cube root for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void cbrt(cuda::std::optional* out, cuda::std::optional const* a) { @@ -51,27 +49,23 @@ __device__ void cbrt(cuda::std::optional* out, cuda::std::optional const* /** * @brief Computes ceiling. * - * Scalar overloads support float and double, a decimal overload preserves scale, and an optional - * overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void ceil(float* out, float const* a) { *out = ::ceilf(*a); } - -/** - * @brief Computes ceiling for double input. - * - * @param out Destination for the computed value. - * @param a Input value. - */ -__device__ inline void ceil(double* out, double const* a) { *out = ::ceil(*a); } +template +__device__ inline void ceil(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::ceil(*a); +} /** - * @brief Computes ceiling for decimal input. + * @brief Computes ceiling. * * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Input decimal value. + * @param out Result destination. + * @param a Input value. */ template __device__ void ceil(numeric::decimal* out, numeric::decimal const* a) @@ -92,11 +86,11 @@ __device__ void ceil(numeric::decimal* out, numeric::decimal const* a) } /** - * @brief Computes ceiling for optional input. + * @brief Computes ceiling. * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. */ template __device__ void ceil(cuda::std::optional* out, cuda::std::optional const* a) @@ -113,27 +107,24 @@ __device__ void ceil(cuda::std::optional* out, cuda::std::optional const* /** * @brief Computes natural exponential. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void exp(float* out, float const* a) { *out = ::expf(*a); } +template +__device__ inline void exp(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::exp(*a); +} /** - * @brief Computes natural exponential for double input. + * @brief Computes natural exponential. * - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void exp(double* out, double const* a) { *out = ::exp(*a); } - -/** - * @brief Computes natural exponential for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void exp(cuda::std::optional* out, cuda::std::optional const* a) { @@ -147,29 +138,25 @@ __device__ void exp(cuda::std::optional* out, cuda::std::optional const* a } /** - * @brief Computes floor. - * - * Scalar overloads support float and double, a decimal overload preserves scale, and an optional - * overload propagates nulls. - * @param out Destination for the computed value. - * @param a Input value. - */ -__device__ inline void floor(float* out, float const* a) { *out = ::floorf(*a); } - -/** - * @brief Computes floor for double input. + * @brief Computes floor of a value. * - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void floor(double* out, double const* a) { *out = ::floor(*a); } +template +__device__ inline void floor(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::floor(*a); +} /** - * @brief Computes floor for decimal input. + * @brief Computes floor of a value. * * @tparam R Decimal representation type. - * @param out Destination decimal value. - * @param a Input decimal value. + * @param out Result destination. + * @param a Input value. */ template __device__ void floor(numeric::decimal* out, numeric::decimal const* a) @@ -190,11 +177,11 @@ __device__ void floor(numeric::decimal* out, numeric::decimal const* a) } /** - * @brief Computes floor for optional input. + * @brief Computes floor of a value. * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. */ template __device__ void floor(cuda::std::optional* out, cuda::std::optional const* a) @@ -211,27 +198,24 @@ __device__ void floor(cuda::std::optional* out, cuda::std::optional const* /** * @brief Computes natural logarithm. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void log(float* out, float const* a) { *out = ::logf(*a); } +template +__device__ inline void log(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::log(*a); +} /** - * @brief Computes natural logarithm for double input. + * @brief Computes natural logarithm. * - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void log(double* out, double const* a) { *out = ::log(*a); } - -/** - * @brief Computes natural logarithm for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void log(cuda::std::optional* out, cuda::std::optional const* a) { @@ -247,30 +231,26 @@ __device__ void log(cuda::std::optional* out, cuda::std::optional const* a /** * @brief Computes exponentiation. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Base value. * @param b Exponent value. */ -__device__ inline void pow(float* out, float const* a, float const* b) { *out = ::powf(*a, *b); } +template +__device__ inline void pow(T* out, T const* a, T const* b) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::pow(*a, *b); +} /** - * @brief Computes exponentiation for double input. + * @brief Computes exponentiation. * - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Base value. * @param b Exponent value. */ -__device__ inline void pow(double* out, double const* a, double const* b) { *out = ::pow(*a, *b); } - -/** - * @brief Computes exponentiation for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional base value. - * @param b Optional exponent value. - */ template __device__ void pow(cuda::std::optional* out, cuda::std::optional const* a, @@ -286,29 +266,26 @@ __device__ void pow(cuda::std::optional* out, } /** - * @brief Rounds to integral value using current rounding mode. + * @brief Rounds to integral value. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void rint(float* out, float const* a) { *out = ::rintf(*a); } +template +__device__ inline void rint(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::rint(*a); +} /** - * @brief Rounds to integral value for double input. + * @brief Rounds to integral value. * - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void rint(double* out, double const* a) { *out = ::rint(*a); } - -/** - * @brief Rounds to integral value for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void rint(cuda::std::optional* out, cuda::std::optional const* a) { @@ -324,27 +301,23 @@ __device__ void rint(cuda::std::optional* out, cuda::std::optional const* /** * @brief Computes square root. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @param out Result destination. * @param a Input value. */ -__device__ inline void sqrt(float* out, float const* a) { *out = ::sqrtf(*a); } +template +__device__ inline void sqrt(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::sqrt(*a); +} /** - * @brief Computes square root for double input. + * @brief Computes square root. * - * @param out Destination for the computed value. + * @tparam T Value type. + * @param out Result destination. * @param a Input value. */ -__device__ inline void sqrt(double* out, double const* a) { *out = ::sqrt(*a); } - -/** - * @brief Computes square root for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void sqrt(cuda::std::optional* out, cuda::std::optional const* a) { diff --git a/cpp/include/cudf/operators/null_handling.cuh b/cpp/include/cudf/operators/null_handling.cuh index f74afde0c9fc..bea020cdcfbc 100644 --- a/cpp/include/cudf/operators/null_handling.cuh +++ b/cpp/include/cudf/operators/null_handling.cuh @@ -13,10 +13,10 @@ namespace CUDF_EXPORT cudf { namespace ops { /** - * @brief Returns false for non-optional inputs. + * @brief Tests whether an input value is null. * * @tparam T Input type. - * @param out Destination for the null test result. + * @param out Result destination. * @param a Input value. */ template @@ -26,11 +26,11 @@ __device__ void is_null(bool* out, T const* a) } /** - * @brief Tests whether an optional input is null. + * @brief Tests whether an input value is null. * - * @tparam T Input value type. - * @param out Destination optional boolean result. - * @param a Optional input value. + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. */ template __device__ void is_null(cuda::std::optional* out, cuda::std::optional const* a) @@ -42,9 +42,9 @@ __device__ void is_null(cuda::std::optional* out, cuda::std::optional c * @brief Sets the output to null when the condition is true. * * @tparam T Value type. - * @param out Destination optional value. - * @param a Optional input value. - * @param condition Optional boolean condition. + * @param out Result destination. + * @param a Input value. + * @param condition boolean condition. */ template __device__ void nullify_if(cuda::std::optional* out, @@ -63,10 +63,10 @@ __device__ void nullify_if(cuda::std::optional* out, } /** - * @brief Returns the first non-null input value of two non-nullable values. + * @brief Returns the first non-null of two values. * * @tparam T Value type. - * @param out Destination value. + * @param out Result destination. * @param a First value. * @param b Second value. */ @@ -77,12 +77,12 @@ __device__ void coalesce(T* out, T const* a, T const* b) } /** - * @brief Returns the first non-null optional value of two optional values, otherwise null. + * @brief Returns the first non-null of two values. * * @tparam T Value type. - * @param out Destination optional value. - * @param a First optional value. - * @param b Second optional value. + * @param out Result destination. + * @param a First value. + * @param b Second value. */ template __device__ void coalesce(cuda::std::optional* out, @@ -99,9 +99,9 @@ __device__ void coalesce(cuda::std::optional* out, } /** - * @brief Returns the input boolean predicate unchanged. + * @brief Converts an optional predicate to a non-nullable predicate. * - * @param out Destination boolean predicate. + * @param out Result destination. * @param a Input boolean predicate. */ __device__ inline void predicate(bool* out, bool const* a) { *out = *a; } @@ -109,7 +109,7 @@ __device__ inline void predicate(bool* out, bool const* a) { *out = *a; } /** * @brief Converts an optional predicate to a non-nullable predicate. * - * @param out Destination optional boolean predicate. + * @param out Result destination. * @param a Optional input boolean predicate. */ __device__ inline void predicate(cuda::std::optional* out, cuda::std::optional const* a) diff --git a/cpp/include/cudf/operators/trigonometric.cuh b/cpp/include/cudf/operators/trigonometric.cuh index 0f5dfda2600c..3a2f55140ddd 100644 --- a/cpp/include/cudf/operators/trigonometric.cuh +++ b/cpp/include/cudf/operators/trigonometric.cuh @@ -6,6 +6,7 @@ #include +#include #include namespace CUDF_EXPORT cudf { @@ -14,27 +15,24 @@ namespace ops { /** * @brief Computes inverse cosine. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arccos(float* out, float const* a) { *out = ::acosf(*a); } +template +__device__ inline void arccos(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::acos(*a); +} /** - * @brief Computes inverse cosine for double input. + * @brief Computes inverse cosine. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arccos(double* out, double const* a) { *out = ::acos(*a); } - -/** - * @brief Computes inverse cosine for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void arccos(cuda::std::optional* out, cuda::std::optional const* a) { @@ -50,27 +48,24 @@ __device__ void arccos(cuda::std::optional* out, cuda::std::optional const /** * @brief Computes inverse hyperbolic cosine. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arccosh(float* out, float const* a) { *out = ::acoshf(*a); } +template +__device__ inline void arccosh(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::acosh(*a); +} /** - * @brief Computes inverse hyperbolic cosine for double input. + * @brief Computes inverse hyperbolic cosine. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arccosh(double* out, double const* a) { *out = ::acosh(*a); } - -/** - * @brief Computes inverse hyperbolic cosine for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void arccosh(cuda::std::optional* out, cuda::std::optional const* a) { @@ -86,27 +81,24 @@ __device__ void arccosh(cuda::std::optional* out, cuda::std::optional cons /** * @brief Computes inverse sine. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arcsin(float* out, float const* a) { *out = ::asinf(*a); } +template +__device__ inline void arcsin(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::asin(*a); +} /** - * @brief Computes inverse sine for double input. + * @brief Computes inverse sine. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arcsin(double* out, double const* a) { *out = ::asin(*a); } - -/** - * @brief Computes inverse sine for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void arcsin(cuda::std::optional* out, cuda::std::optional const* a) { @@ -122,27 +114,24 @@ __device__ void arcsin(cuda::std::optional* out, cuda::std::optional const /** * @brief Computes inverse hyperbolic sine. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arcsinh(float* out, float const* a) { *out = ::asinhf(*a); } +template +__device__ inline void arcsinh(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::asinh(*a); +} /** - * @brief Computes inverse hyperbolic sine for double input. + * @brief Computes inverse hyperbolic sine. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arcsinh(double* out, double const* a) { *out = ::asinh(*a); } - -/** - * @brief Computes inverse hyperbolic sine for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void arcsinh(cuda::std::optional* out, cuda::std::optional const* a) { @@ -158,27 +147,24 @@ __device__ void arcsinh(cuda::std::optional* out, cuda::std::optional cons /** * @brief Computes inverse tangent. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arctan(float* out, float const* a) { *out = ::atanf(*a); } +template +__device__ inline void arctan(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::atan(*a); +} /** - * @brief Computes inverse tangent for double input. + * @brief Computes inverse tangent. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arctan(double* out, double const* a) { *out = ::atan(*a); } - -/** - * @brief Computes inverse tangent for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void arctan(cuda::std::optional* out, cuda::std::optional const* a) { @@ -194,27 +180,24 @@ __device__ void arctan(cuda::std::optional* out, cuda::std::optional const /** * @brief Computes inverse hyperbolic tangent. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arctanh(float* out, float const* a) { *out = ::atanhf(*a); } +template +__device__ inline void arctanh(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::atanh(*a); +} /** - * @brief Computes inverse hyperbolic tangent for double input. + * @brief Computes inverse hyperbolic tangent. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void arctanh(double* out, double const* a) { *out = ::atanh(*a); } - -/** - * @brief Computes inverse hyperbolic tangent for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void arctanh(cuda::std::optional* out, cuda::std::optional const* a) { @@ -230,27 +213,24 @@ __device__ void arctanh(cuda::std::optional* out, cuda::std::optional cons /** * @brief Computes cosine. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void cos(float* out, float const* a) { *out = ::cosf(*a); } +template +__device__ inline void cos(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::cos(*a); +} /** - * @brief Computes cosine for double input. + * @brief Computes cosine. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void cos(double* out, double const* a) { *out = ::cos(*a); } - -/** - * @brief Computes cosine for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void cos(cuda::std::optional* out, cuda::std::optional const* a) { @@ -266,27 +246,24 @@ __device__ void cos(cuda::std::optional* out, cuda::std::optional const* a /** * @brief Computes hyperbolic cosine. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void cosh(float* out, float const* a) { *out = ::coshf(*a); } +template +__device__ inline void cosh(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::cosh(*a); +} /** - * @brief Computes hyperbolic cosine for double input. + * @brief Computes hyperbolic cosine. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void cosh(double* out, double const* a) { *out = ::cosh(*a); } - -/** - * @brief Computes hyperbolic cosine for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void cosh(cuda::std::optional* out, cuda::std::optional const* a) { @@ -302,27 +279,24 @@ __device__ void cosh(cuda::std::optional* out, cuda::std::optional const* /** * @brief Computes sine. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void sin(float* out, float const* a) { *out = ::sinf(*a); } +template +__device__ inline void sin(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::sin(*a); +} /** - * @brief Computes sine for double input. + * @brief Computes sine. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void sin(double* out, double const* a) { *out = ::sin(*a); } - -/** - * @brief Computes sine for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void sin(cuda::std::optional* out, cuda::std::optional const* a) { @@ -338,27 +312,24 @@ __device__ void sin(cuda::std::optional* out, cuda::std::optional const* a /** * @brief Computes hyperbolic sine. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void sinh(float* out, float const* a) { *out = ::sinhf(*a); } +template +__device__ inline void sinh(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::sinh(*a); +} /** - * @brief Computes hyperbolic sine for double input. + * @brief Computes hyperbolic sine. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void sinh(double* out, double const* a) { *out = ::sinh(*a); } - -/** - * @brief Computes hyperbolic sine for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void sinh(cuda::std::optional* out, cuda::std::optional const* a) { @@ -374,27 +345,24 @@ __device__ void sinh(cuda::std::optional* out, cuda::std::optional const* /** * @brief Computes tangent. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void tan(float* out, float const* a) { *out = ::tanf(*a); } +template +__device__ inline void tan(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::tan(*a); +} /** - * @brief Computes tangent for double input. + * @brief Computes tangent. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void tan(double* out, double const* a) { *out = ::tan(*a); } - -/** - * @brief Computes tangent for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void tan(cuda::std::optional* out, cuda::std::optional const* a) { @@ -410,27 +378,24 @@ __device__ void tan(cuda::std::optional* out, cuda::std::optional const* a /** * @brief Computes hyperbolic tangent. * - * Scalar overloads support float and double inputs, and an optional overload propagates nulls. - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void tanh(float* out, float const* a) { *out = ::tanhf(*a); } +template +__device__ inline void tanh(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = cuda::std::tanh(*a); +} /** - * @brief Computes hyperbolic tangent for double input. + * @brief Computes hyperbolic tangent. * - * @param out Destination for the computed value. + * @tparam T Value type + * @param out Result destination. * @param a Input value. */ -__device__ inline void tanh(double* out, double const* a) { *out = ::tanh(*a); } - -/** - * @brief Computes hyperbolic tangent for optional input. - * - * @tparam T Input and output type. - * @param out Destination optional value. - * @param a Optional input value. - */ template __device__ void tanh(cuda::std::optional* out, cuda::std::optional const* a) { From 1653923d0353761690e83b89615d405ba3836427 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 28 May 2026 15:22:59 +0000 Subject: [PATCH 23/34] Fix precision check and update requirements for fixed point types in arithmetic operations --- cpp/include/cudf/operators/ansi_arithmetic.cuh | 2 +- cpp/include/cudf/operators/arithmetic.cuh | 4 ++-- cpp/include/cudf/operators/math.cuh | 14 ++++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index cfcbafa6316d..1f8ae94ecfd6 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -598,7 +598,7 @@ __device__ errc ansi_precision_check(numeric::decimal* out, auto abs_value = value < 0 ? -value : value; - if (abs_value >= numeric::detail::ipow(static_cast(*precision))) { + if (abs_value >= numeric::detail::ipow(*precision)) { return errc::OVERFLOW; } diff --git a/cpp/include/cudf/operators/arithmetic.cuh b/cpp/include/cudf/operators/arithmetic.cuh index 683302e55f9b..283f4ae763ce 100644 --- a/cpp/include/cudf/operators/arithmetic.cuh +++ b/cpp/include/cudf/operators/arithmetic.cuh @@ -194,7 +194,7 @@ __device__ void floor_div(cuda::std::optional* out, */ template __device__ void mod(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v || cudf::is_fixed_point) + requires(cuda::std::is_integral_v || cudf::is_fixed_point()) { *out = (*a % *b); } @@ -246,7 +246,7 @@ __device__ void mod(cuda::std::optional* out, */ template __device__ void pymod(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v || cudf::is_fixed_point) + requires(cuda::std::is_integral_v || cudf::is_fixed_point()) { *out = (*a % *b + *b) % *b; } diff --git a/cpp/include/cudf/operators/math.cuh b/cpp/include/cudf/operators/math.cuh index 0733e9107094..996587203315 100644 --- a/cpp/include/cudf/operators/math.cuh +++ b/cpp/include/cudf/operators/math.cuh @@ -73,9 +73,10 @@ __device__ void ceil(numeric::decimal* out, numeric::decimal const* a) if (a->scale() >= 0) { *out = *a; } else { - auto factor = detail::ipow10(-static_cast(a->scale())); - auto div = a->value() / factor; - auto rem = a->value() % factor; + auto factor = + numeric::detail::ipow(-static_cast(a->scale())); + auto div = a->value() / factor; + auto rem = a->value() % factor; if (rem == 0) { *out = *a; } else { @@ -164,9 +165,10 @@ __device__ void floor(numeric::decimal* out, numeric::decimal const* a) if (a->scale() >= 0) { *out = *a; } else { - auto factor = numeric::detail::ipow(-static_cast(a->scale())); - auto div = a->value() / factor; - auto rem = a->value() % factor; + auto factor = + numeric::detail::ipow(-static_cast(a->scale())); + auto div = a->value() / factor; + auto rem = a->value() % factor; if (rem == 0) { *out = *a; } else { From c96c444ec8f5ea2cba163fc7a87679cffb5bf621 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Thu, 28 May 2026 20:43:14 +0000 Subject: [PATCH 24/34] Fix overflow checks in ansi_add and update comparison functions to return nullopt for non-existent values --- .../cudf/operators/ansi_arithmetic.cuh | 24 +++++++++++++++---- cpp/include/cudf/operators/comparison.cuh | 20 +++++++--------- cpp/include/cudf/operators/null_handling.cuh | 2 +- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index 1f8ae94ecfd6..25a195471182 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -31,7 +31,7 @@ __device__ errc ansi_add(T* out, T const* a, T const* b) requires(cuda::std::is_integral_v) { T r; - if (cuda::add_overflow(r, *a, *b)) { return errc::OVERFLOW; } + if (cuda::add_overflow(r, *a, *b).overflow) { return errc::OVERFLOW; } *out = r; return errc::SUCCESS; } @@ -377,7 +377,7 @@ __device__ errc ansi_mod(T* out, T const* a, T const* b) * * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. */ -__device__ errc ansi_mod(float* out, float const* a, float const* b) +__device__ inline errc ansi_mod(float* out, float const* a, float const* b) { if (*b == 0) { return errc::DIVISION_BY_ZERO; } *out = (*a) - (*b) * ::floorf((*a) / (*b)); @@ -389,7 +389,7 @@ __device__ errc ansi_mod(float* out, float const* a, float const* b) * * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. */ -__device__ errc ansi_mod(double* out, double const* a, double const* b) +__device__ inline errc ansi_mod(double* out, double const* a, double const* b) { if (*b == 0) { return errc::DIVISION_BY_ZERO; } *out = (*a) - (*b) * ::floor((*a) / (*b)); @@ -533,13 +533,29 @@ __device__ errc ansi_abs(cuda::std::optional* out, cuda::std::optional con */ template __device__ errc ansi_neg(T* out, T const* a) - requires(cuda::std::is_signed_v) + requires(cuda::std::is_integral_v && cuda::std::is_signed_v) { if (*a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } *out = -(*a); return errc::SUCCESS; } +/** + * @brief Computes unary negation with ANSI overflow checks. + * + * @tparam T Value type. + * @param out Result destination. + * @param a Input value. + * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + */ +template +__device__ errc ansi_neg(T* out, T const* a) + requires(cuda::std::is_floating_point_v) +{ + *out = -(*a); + return errc::SUCCESS; +} + /** * @brief Computes unary negation with ANSI overflow checks. * diff --git a/cpp/include/cudf/operators/comparison.cuh b/cpp/include/cudf/operators/comparison.cuh index 432cfed72d4f..facb0c3726d5 100644 --- a/cpp/include/cudf/operators/comparison.cuh +++ b/cpp/include/cudf/operators/comparison.cuh @@ -42,10 +42,8 @@ __device__ void equal(cuda::std::optional* out, bool r; equal(&r, &a->value(), &b->value()); *out = r; - } else if (!a->has_value() && !b->has_value()) { - *out = true; } else { - *out = false; + *out = cuda::std::nullopt; } } @@ -80,10 +78,8 @@ __device__ void not_equal(cuda::std::optional* out, bool r; not_equal(&r, &a->value(), &b->value()); *out = r; - } else if (!a->has_value() && !b->has_value()) { - *out = false; } else { - *out = true; + *out = cuda::std::nullopt; } } @@ -119,7 +115,7 @@ __device__ void greater(cuda::std::optional* out, greater(&r, &a->value(), &b->value()); *out = r; } else { - *out = false; + *out = cuda::std::nullopt; } } @@ -155,7 +151,7 @@ __device__ void greater_equal(cuda::std::optional* out, greater_equal(&r, &a->value(), &b->value()); *out = r; } else { - *out = false; + *out = cuda::std::nullopt; } } @@ -191,7 +187,7 @@ __device__ void less(cuda::std::optional* out, less(&r, &a->value(), &b->value()); *out = r; } else { - *out = false; + *out = cuda::std::nullopt; } } @@ -227,7 +223,7 @@ __device__ void less_equal(cuda::std::optional* out, less_equal(&r, &a->value(), &b->value()); *out = r; } else { - *out = false; + *out = cuda::std::nullopt; } } @@ -259,7 +255,9 @@ __device__ void null_equal(cuda::std::optional* out, cuda::std::optional const* b) { if (a->has_value() && b->has_value()) { - *out = (*(*a) == *(*b)); + bool r; + null_equal(&r, &a->value(), &b->value()); + *out = r; } else if (!a->has_value() && !b->has_value()) { *out = true; } else { diff --git a/cpp/include/cudf/operators/null_handling.cuh b/cpp/include/cudf/operators/null_handling.cuh index bea020cdcfbc..06a876e75043 100644 --- a/cpp/include/cudf/operators/null_handling.cuh +++ b/cpp/include/cudf/operators/null_handling.cuh @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */ From da536670578ca70dabd10c81fb3bbb7681aec7a1 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Fri, 29 May 2026 11:13:00 +0000 Subject: [PATCH 25/34] Add all.cuh header file with operator includes --- cpp/include/cudf/operators/all.cuh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 cpp/include/cudf/operators/all.cuh diff --git a/cpp/include/cudf/operators/all.cuh b/cpp/include/cudf/operators/all.cuh new file mode 100644 index 000000000000..2ebd187073aa --- /dev/null +++ b/cpp/include/cudf/operators/all.cuh @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include From d7bd54875ec5dedf4fdc20dfa7198719d91eadf6 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Sat, 30 May 2026 18:51:47 +0000 Subject: [PATCH 26/34] merge AST operators into operator library --- .../cudf/ast/detail/expression_evaluator.cuh | 16 +- .../cudf/ast/detail/operator_functor.cuh | 877 +++--------------- cpp/include/cudf/ast/detail/operators.cuh | 6 +- .../cudf/operators/ansi_arithmetic.cuh | 702 +++----------- cpp/include/cudf/operators/arithmetic.cuh | 327 ++----- cpp/include/cudf/operators/bitwise.cuh | 175 +--- cpp/include/cudf/operators/casts.cuh | 399 +------- cpp/include/cudf/operators/comparison.cuh | 194 +--- cpp/include/cudf/operators/concepts.cuh | 54 ++ cpp/include/cudf/operators/error.hpp | 87 ++ cpp/include/cudf/operators/identity.cuh | 7 +- cpp/include/cudf/operators/logic.cuh | 165 +--- cpp/include/cudf/operators/math.cuh | 255 +---- cpp/include/cudf/operators/null_handling.cuh | 70 +- cpp/include/cudf/operators/trigonometric.cuh | 328 +------ cpp/include/cudf/operators/types.cuh | 18 + cpp/src/ast/operators.cpp | 7 +- cpp/src/jit/row_ir.cpp | 3 +- 18 files changed, 747 insertions(+), 2943 deletions(-) create mode 100644 cpp/include/cudf/operators/concepts.cuh create mode 100644 cpp/include/cudf/operators/types.cuh diff --git a/cpp/include/cudf/ast/detail/expression_evaluator.cuh b/cpp/include/cudf/ast/detail/expression_evaluator.cuh index 428b84eb03bc..1a95bf16382f 100644 --- a/cpp/include/cudf/ast/detail/expression_evaluator.cuh +++ b/cpp/include/cudf/ast/detail/expression_evaluator.cuh @@ -719,7 +719,7 @@ struct expression_evaluator { typename ResultSubclass, typename T, bool result_has_nulls, - CUDF_ENABLE_IF(detail::is_valid_unary_op, + CUDF_ENABLE_IF(detail::is_valid_unary_op, possibly_null_value_t>)> __device__ inline void operator()( expression_result& output_object, @@ -730,19 +730,19 @@ struct expression_evaluator { { // The output data type is the same whether or not nulls are present, so // pull from the non-nullable operator. - using Out = cuda::std::invoke_result_t, Input>; + using Out = cuda::std::invoke_result_t, Input>; this->template resolve_output(output_object, output, output_row_index, thread_intermediate_storage, - detail::operator_functor{}(input)); + detail::operator_functor{}(input)); } template , + CUDF_ENABLE_IF(!detail::is_valid_unary_op, possibly_null_value_t>)> __device__ inline void operator()( expression_result& output_object, @@ -781,7 +781,7 @@ struct expression_evaluator { typename ResultSubclass, typename T, bool result_has_nulls, - CUDF_ENABLE_IF(detail::is_valid_binary_op, + CUDF_ENABLE_IF(detail::is_valid_binary_op, possibly_null_value_t, possibly_null_value_t>)> __device__ inline void operator()( @@ -794,19 +794,19 @@ struct expression_evaluator { { // The output data type is the same whether or not nulls are present, so // pull from the non-nullable operator. - using Out = cuda::std::invoke_result_t, LHS, RHS>; + using Out = cuda::std::invoke_result_t, LHS, RHS>; this->template resolve_output(output_object, output, output_row_index, thread_intermediate_storage, - detail::operator_functor{}(lhs, rhs)); + detail::operator_functor{}(lhs, rhs)); } template , + CUDF_ENABLE_IF(!detail::is_valid_binary_op, possibly_null_value_t, possibly_null_value_t>)> __device__ inline void operator()( diff --git a/cpp/include/cudf/ast/detail/operator_functor.cuh b/cpp/include/cudf/ast/detail/operator_functor.cuh index e245f0288be6..e559f841f048 100644 --- a/cpp/include/cudf/ast/detail/operator_functor.cuh +++ b/cpp/include/cudf/ast/detail/operator_functor.cuh @@ -5,25 +5,91 @@ #pragma once #include -#include -#include -#include -#include +#include +#include #include #include -#include +#include +#include namespace CUDF_EXPORT cudf { namespace ast::detail { +template +struct operator_invoker; + +#define CUDF_AST_OPERATOR_MAP(OP, func_name, num_args) \ + template <> \ + struct operator_invoker { \ + static constexpr auto arity = num_args; \ + __device__ static inline auto eval(auto... a) \ + requires(requires { cudf::ops::func_name(a...); }) \ + { \ + return cudf::ops::func_name(a...); \ + } \ + }; + +CUDF_AST_OPERATOR_MAP(ADD, add, 2) +CUDF_AST_OPERATOR_MAP(SUB, sub, 2) +CUDF_AST_OPERATOR_MAP(MUL, mul, 2) +CUDF_AST_OPERATOR_MAP(DIV, div, 2) +CUDF_AST_OPERATOR_MAP(TRUE_DIV, true_div, 2) +CUDF_AST_OPERATOR_MAP(FLOOR_DIV, floor_div, 2) +CUDF_AST_OPERATOR_MAP(MOD, mod, 2) +CUDF_AST_OPERATOR_MAP(PYMOD, pymod, 2) +CUDF_AST_OPERATOR_MAP(POW, pow, 2) +CUDF_AST_OPERATOR_MAP(EQUAL, equal, 2) +CUDF_AST_OPERATOR_MAP(NOT_EQUAL, not_equal, 2) +CUDF_AST_OPERATOR_MAP(LESS, less, 2) +CUDF_AST_OPERATOR_MAP(GREATER, greater, 2) +CUDF_AST_OPERATOR_MAP(LESS_EQUAL, less_equal, 2) +CUDF_AST_OPERATOR_MAP(GREATER_EQUAL, greater_equal, 2) +CUDF_AST_OPERATOR_MAP(BITWISE_AND, bit_and, 2) +CUDF_AST_OPERATOR_MAP(BITWISE_OR, bit_or, 2) +CUDF_AST_OPERATOR_MAP(BITWISE_XOR, bit_xor, 2) +CUDF_AST_OPERATOR_MAP(LOGICAL_AND, logical_and, 2) +CUDF_AST_OPERATOR_MAP(LOGICAL_OR, logical_or, 2) +CUDF_AST_OPERATOR_MAP(IDENTITY, identity, 1) +CUDF_AST_OPERATOR_MAP(SIN, sin, 1) +CUDF_AST_OPERATOR_MAP(COS, cos, 1) +CUDF_AST_OPERATOR_MAP(TAN, tan, 1) +CUDF_AST_OPERATOR_MAP(ARCSIN, arcsin, 1) +CUDF_AST_OPERATOR_MAP(ARCCOS, arccos, 1) +CUDF_AST_OPERATOR_MAP(ARCTAN, arctan, 1) +CUDF_AST_OPERATOR_MAP(SINH, sinh, 1) +CUDF_AST_OPERATOR_MAP(COSH, cosh, 1) +CUDF_AST_OPERATOR_MAP(TANH, tanh, 1) +CUDF_AST_OPERATOR_MAP(ARCSINH, arcsinh, 1) +CUDF_AST_OPERATOR_MAP(ARCCOSH, arccosh, 1) +CUDF_AST_OPERATOR_MAP(ARCTANH, arctanh, 1) +CUDF_AST_OPERATOR_MAP(EXP, exp, 1) +CUDF_AST_OPERATOR_MAP(LOG, log, 1) +CUDF_AST_OPERATOR_MAP(SQRT, sqrt, 1) +CUDF_AST_OPERATOR_MAP(CBRT, cbrt, 1) +CUDF_AST_OPERATOR_MAP(CEIL, ceil, 1) +CUDF_AST_OPERATOR_MAP(FLOOR, floor, 1) +CUDF_AST_OPERATOR_MAP(ABS, abs, 1) +CUDF_AST_OPERATOR_MAP(RINT, rint, 1) +CUDF_AST_OPERATOR_MAP(BIT_INVERT, bit_invert, 1) +CUDF_AST_OPERATOR_MAP(NOT, logical_not, 1) +CUDF_AST_OPERATOR_MAP(CAST_TO_INT64, cast_to_i64, 1) +CUDF_AST_OPERATOR_MAP(CAST_TO_UINT64, cast_to_u64, 1) +CUDF_AST_OPERATOR_MAP(CAST_TO_FLOAT64, cast_to_f64, 1) +CUDF_AST_OPERATOR_MAP(IS_NULL, is_null, 1) +CUDF_AST_OPERATOR_MAP(NULL_EQUAL, null_equal, 2) +CUDF_AST_OPERATOR_MAP(NULL_LOGICAL_AND, null_logical_and, 2) +CUDF_AST_OPERATOR_MAP(NULL_LOGICAL_OR, null_logical_or, 2) + +#undef CUDF_AST_OPERATOR_MAP + /** * @brief Operator functor. * * This functor is templated on an `ast_operator`, with each template specialization defining a - * callable `operator()` that executes the operation. The functor specialization also has a member - * `arity` defining the number of operands that are accepted by the call to `operator()`. The - * `operator()` is templated on the types of its inputs (e.g. `typename LHS` and `typename RHS` for + * callable `eval` that executes the operation. The functor specialization also has a member + * `arity` defining the number of operands that are accepted by the call to `eval`. The + * `eval` is templated on the types of its inputs (e.g. `typename LHS` and `typename RHS` for * a binary operator). Trailing return types are defined as `decltype(result)` where `result` is * the returned value. The trailing return types allow SFINAE to only consider template * instantiations for valid combinations of types. This, in turn, allows the operator functors to be @@ -31,759 +97,84 @@ namespace ast::detail { * * @tparam op AST operator. */ -template -struct operator_functor {}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs + rhs) - { - return lhs + rhs; - } - - static constexpr int32_t fixed_point_result_scale(int32_t lhs, int32_t rhs) - { - return cuda::std::min(lhs, rhs); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs - rhs) - { - return lhs - rhs; - } - - static constexpr int32_t fixed_point_result_scale(int32_t lhs, int32_t rhs) - { - return cuda::std::min(lhs, rhs); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs * rhs) - { - return lhs * rhs; - } - - static constexpr int32_t fixed_point_result_scale(int32_t lhs, int32_t rhs) { return lhs + rhs; } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs / rhs) - { - return lhs / rhs; - } - - static constexpr int32_t fixed_point_result_scale(int32_t lhs, int32_t rhs) { return lhs - rhs; } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept - -> decltype(static_cast(lhs) / static_cast(rhs)) - { - return static_cast(lhs) / static_cast(rhs); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; +template +struct operator_functor { + static constexpr auto arity = operator_invoker::arity; - template > - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> CommonType - requires(cuda::std::is_integral_v) + template + __device__ inline auto operator()(T a) + requires(!cudf::ops::nullable && requires { operator_invoker::eval(a); }) { - return cudf::detail::integral_floor_div(lhs, rhs); + return operator_invoker::eval(a); } - template > - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> CommonType - requires(cuda::std::is_floating_point_v) + template + __device__ inline auto operator()(T a) + requires( + cudf::ops::nullable && (requires { operator_invoker::eval(a); } || + requires { operator_invoker::eval(a.value()); }) + ) { - if constexpr (cuda::std::is_same_v) { - return cuda::std::floorf(static_cast(lhs) / static_cast(rhs)); + // If the operator is not defined for optional, but is defined for T then it is assumed to be + // null-propagating. + if constexpr (requires { operator_invoker::eval(a); }) { + return operator_invoker::eval(a); + } else { + using result_t = cudf::ops::optional::eval(a.value()))>; + if (a.has_value()) { + return result_t{operator_invoker::eval(a.value())}; + } else { + return result_t{}; + } } - return cuda::std::floor(static_cast(lhs) / static_cast(rhs)); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template > - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept - -> decltype(static_cast(lhs) % static_cast(rhs)) - requires(cuda::std::is_integral_v) - { - return static_cast(lhs) % static_cast(rhs); - } - - template > - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept - -> decltype(fmodf(static_cast(lhs), static_cast(rhs))) - requires(cuda::std::is_same_v) - { - return fmodf(static_cast(lhs), static_cast(rhs)); - } - - template > - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept - -> decltype(fmod(static_cast(lhs), static_cast(rhs))) - requires(cuda::std::is_same_v) - { - return fmod(static_cast(lhs), static_cast(rhs)); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template > - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept - -> decltype(((static_cast(lhs) % static_cast(rhs)) + - static_cast(rhs)) % - static_cast(rhs)) - requires(cuda::std::is_integral_v) - { - return ((static_cast(lhs) % static_cast(rhs)) + - static_cast(rhs)) % - static_cast(rhs); - } - - template > - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept - -> decltype(fmodf(fmodf(static_cast(lhs), static_cast(rhs)) + - static_cast(rhs), - static_cast(rhs))) - requires(cuda::std::is_same_v) - { - return fmodf(fmodf(static_cast(lhs), static_cast(rhs)) + - static_cast(rhs), - static_cast(rhs)); - } - - template > - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept - -> decltype(fmod(fmod(static_cast(lhs), static_cast(rhs)) + - static_cast(rhs), - static_cast(rhs))) - requires(cuda::std::is_same_v) - { - return fmod(fmod(static_cast(lhs), static_cast(rhs)) + - static_cast(rhs), - static_cast(rhs)); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> LHS - requires(cuda::std::is_integral_v and cuda::std::is_integral_v) - { - return cudf::detail::integral_pow(lhs, rhs); - } - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept - -> decltype(cuda::std::pow(lhs, rhs)) - requires(not(cuda::std::is_integral_v and cuda::std::is_integral_v)) - { - return cuda::std::pow(lhs, rhs); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs == rhs) - { - return lhs == rhs; - } -}; - -// Alias NULL_EQUAL = EQUAL in the non-nullable case. -template <> -struct operator_functor - : public operator_functor {}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs != rhs) - { - return lhs != rhs; - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs < rhs) - { - return lhs < rhs; - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs > rhs) - { - return lhs > rhs; } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs <= rhs) - { - return lhs <= rhs; - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs >= rhs) - { - return lhs >= rhs; - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs & rhs) + template + __device__ inline auto operator()(T a, T b) + requires(!cudf::ops::nullable && requires { operator_invoker::eval(a, b); }) { - return lhs & rhs; + return operator_invoker::eval(a, b); } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs | rhs) + template + __device__ inline auto operator()(T a, T b) + requires( + cudf::ops::nullable && + (requires { operator_invoker::eval(a, b); } || + requires { operator_invoker::eval(a.value(), b.value()); })) { - return lhs | rhs; - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs ^ rhs) - { - return lhs ^ rhs; - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs && rhs) - { - return lhs && rhs; - } -}; - -// Alias NULL_LOGICAL_AND = LOGICAL_AND in the non-nullable case. -template <> -struct operator_functor - : public operator_functor {}; - -template <> -struct operator_functor { - static constexpr auto arity{2}; - - template - __device__ inline auto operator()(LHS lhs, RHS rhs) const noexcept -> decltype(lhs || rhs) - { - return lhs || rhs; - } -}; - -// Alias NULL_LOGICAL_OR = LOGICAL_OR in the non-nullable case. -template <> -struct operator_functor - : public operator_functor {}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(input) - { - return input; - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> bool - { - return false; - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::sin(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::sin(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::cos(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::cos(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::tan(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::tan(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::asin(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::asin(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::acos(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::acos(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::atan(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::atan(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::sinh(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::sinh(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::cosh(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::cosh(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::tanh(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::tanh(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept - -> decltype(cuda::std::asinh(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::asinh(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept - -> decltype(cuda::std::acosh(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::acosh(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept - -> decltype(cuda::std::atanh(input)) - requires(cuda::std::is_floating_point_v) - { - return cuda::std::atanh(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::exp(input)) - { - return cuda::std::exp(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::log(input)) - { - return cuda::std::log(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::sqrt(input)) - { - return cuda::std::sqrt(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::cbrt(input)) - { - return cuda::std::cbrt(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::ceil(input)) - { - return cuda::std::ceil(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept - -> decltype(cuda::std::floor(input)) - { - return cuda::std::floor(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - // Only accept signed or unsigned types (both require is_arithmetic to be true) - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::abs(input)) - requires(cuda::std::is_signed_v) - { - return cuda::std::abs(input); - } - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(input) - requires(cuda::std::is_unsigned_v) - { - return input; - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(cuda::std::rint(input)) - { - return cuda::std::rint(input); - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(~input) - { - return ~input; - } -}; - -template <> -struct operator_functor { - static constexpr auto arity{1}; - - template - __device__ inline auto operator()(InputT input) const noexcept -> decltype(!input) - { - return !input; - } -}; - -template -struct cast { - static constexpr auto arity{1}; - template - __device__ inline auto operator()(From f) const noexcept -> To - requires(is_fixed_point()) - { - if constexpr (cuda::std::is_floating_point_v) { - return convert_fixed_to_floating(f); + // If the operator is not defined for optional, but is defined for T then it is assumed to be + // null-propagating. + if constexpr (requires { operator_invoker::eval(a, b); }) { + return operator_invoker::eval(a, b); } else { - return static_cast(f); + using result_t = + cudf::ops::optional::eval(a.value(), b.value()))>; + if (a.has_value() && b.has_value()) { + return result_t{operator_invoker::eval(a.value(), b.value())}; + } else { + return result_t{}; + } } } - template - __device__ inline auto operator()(From f) const noexcept -> decltype(static_cast(f)) - requires(!is_fixed_point()) - { - return static_cast(f); - } -}; - -template <> -struct operator_functor : cast {}; -template <> -struct operator_functor : cast {}; -template <> -struct operator_functor : cast {}; - -/* - * The default specialization of nullable operators is to fall back to the non-nullable - * implementation - */ -template -struct operator_functor { - using NonNullOperator = operator_functor; - static constexpr auto arity = NonNullOperator::arity; - - template - __device__ inline auto operator()(LHS const lhs, RHS const rhs) const noexcept - -> possibly_null_value_t - requires(arity_placeholder == 2) - { - using Out = possibly_null_value_t; - return (lhs.has_value() && rhs.has_value()) ? Out{NonNullOperator{}(*lhs, *rhs)} : Out{}; - } - - template - __device__ inline auto operator()(Input const input) const noexcept - -> possibly_null_value_t - requires(arity_placeholder == 1) - { - using Out = possibly_null_value_t; - return input.has_value() ? Out{NonNullOperator{}(*input)} : Out{}; - } -}; - -// IS_NULL(null) is true, IS_NULL(valid) is false -template <> -struct operator_functor { - using NonNullOperator = operator_functor; - static constexpr auto arity = NonNullOperator::arity; - - template - __device__ inline auto operator()(LHS const lhs) const noexcept -> bool - { - return !lhs.has_value(); - } -}; - -// NULL_EQUAL(null, null) is true, NULL_EQUAL(null, valid) is false, and NULL_EQUAL(valid, valid) == -// EQUAL(valid, valid) -template <> -struct operator_functor { - using NonNullOperator = operator_functor; - static constexpr auto arity = NonNullOperator::arity; - - template - __device__ inline auto operator()(LHS const lhs, RHS const rhs) const noexcept - -> possibly_null_value_t - { - // Case 1: Neither is null, so the output is given by the operation. - if (lhs.has_value() && rhs.has_value()) { return {NonNullOperator{}(*lhs, *rhs)}; } - // Case 2: Two nulls compare equal. - if (!lhs.has_value() && !rhs.has_value()) { return {true}; } - // Case 3: One value is null, while the other is not, so we return false. - return {false}; - } -}; - -///< NULL_LOGICAL_AND(null, null) is null, NULL_LOGICAL_AND(null, true) is null, -///< NULL_LOGICAL_AND(null, false) is false, and NULL_LOGICAL_AND(valid, valid) == -///< LOGICAL_AND(valid, valid) -template <> -struct operator_functor { - using NonNullOperator = operator_functor; - static constexpr auto arity = NonNullOperator::arity; - - template - __device__ inline auto operator()(LHS const lhs, RHS const rhs) const noexcept - -> possibly_null_value_t - { - // Case 1: Neither is null, so the output is given by the operation. - if (lhs.has_value() && rhs.has_value()) { return {NonNullOperator{}(*lhs, *rhs)}; } - // Case 2: Two nulls return null. - if (!lhs.has_value() && !rhs.has_value()) { return {}; } - // Case 3: One value is null, while the other is not. If it's true we return null, otherwise we - // return false. - auto const& valid_element = lhs.has_value() ? lhs : rhs; - if (*valid_element) { return {}; } - return {false}; - } -}; - -///< NULL_LOGICAL_OR(null, null) is null, NULL_LOGICAL_OR(null, true) is true, NULL_LOGICAL_OR(null, -///< false) is null, and NULL_LOGICAL_OR(valid, valid) == LOGICAL_OR(valid, valid) -template <> -struct operator_functor { - using NonNullOperator = operator_functor; - static constexpr auto arity = NonNullOperator::arity; - - template - __device__ inline auto operator()(LHS const lhs, RHS const rhs) const noexcept - -> possibly_null_value_t - { - // Case 1: Neither is null, so the output is given by the operation. - if (lhs.has_value() && rhs.has_value()) { return {NonNullOperator{}(*lhs, *rhs)}; } - // Case 2: Two nulls return null. - if (!lhs.has_value() && !rhs.has_value()) { return {}; } - // Case 3: One value is null, while the other is not. If it's true we return true, otherwise we - // return null. - auto const& valid_element = lhs.has_value() ? lhs : rhs; - if (*valid_element) { return {true}; } - return {}; + static constexpr int32_t fixed_point_result_scale(int32_t a, int32_t b) + requires(op == ast_operator::ADD || op == ast_operator::SUB || op == ast_operator::MUL || + op == ast_operator::DIV || op == ast_operator::MOD || op == ast_operator::PYMOD) + { + if constexpr (op == ast_operator::ADD || op == ast_operator::SUB) { + return cuda::std::min(a, b); + } else if constexpr (op == ast_operator::MUL) { + return a + b; + } else if constexpr (op == ast_operator::DIV) { + return a - b; + } else if constexpr (op == ast_operator::MOD) { + return cuda::std::min(a, b); + } else if constexpr (op == ast_operator::PYMOD) { + return cuda::std::min(a, b); + } } }; -constexpr bool predicate(possibly_null_value_t value) { return value; } - -constexpr bool predicate(possibly_null_value_t value) -{ - return value.has_value() && *value; -} - } // namespace ast::detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/ast/detail/operators.cuh b/cpp/include/cudf/ast/detail/operators.cuh index 292a26a56c26..a4879de95edc 100644 --- a/cpp/include/cudf/ast/detail/operators.cuh +++ b/cpp/include/cudf/ast/detail/operators.cuh @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -20,10 +20,10 @@ namespace ast::detail { // Traits for valid operator / type combinations template -constexpr bool is_valid_binary_op = cuda::std::is_invocable_v; +constexpr bool is_valid_binary_op = requires(Op op, LHS lhs, RHS rhs) { op(lhs, rhs); }; template -constexpr bool is_valid_unary_op = cuda::std::is_invocable_v; +constexpr bool is_valid_unary_op = requires(Op op, T value) { op(value); }; /** * @brief Operator dispatcher diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index 25a195471182..0986f116b247 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -5,13 +5,15 @@ #pragma once #include +#include #include #include +#include #include #include +#include #include -#include #include namespace CUDF_EXPORT cudf { @@ -21,255 +23,155 @@ namespace ops { * @brief Adds operands with overflow detection. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template -__device__ errc ansi_add(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v) +template +__device__ result ansi_add(T a, T b) { T r; - if (cuda::add_overflow(r, *a, *b).overflow) { return errc::OVERFLOW; } - *out = r; - return errc::SUCCESS; + if (cuda::add_overflow(r, a, b).overflow) { return errc::OVERFLOW; } + return r; } /** * @brief Adds operands with overflow detection. * * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template -__device__ errc ansi_add(T* out, T const* a, T const* b) - requires(cuda::std::is_floating_point_v) +template +__device__ result ansi_add(T a, T b) { - *out = *a + *b; - return errc::SUCCESS; + return a + b; } /** * @brief Adds operands with overflow detection. * * @tparam R Decimal representation type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ template -__device__ errc ansi_add(numeric::decimal* out, - numeric::decimal const* a, - numeric::decimal const* b) +__device__ result> ansi_add(numeric::decimal a, numeric::decimal b) { - auto scale = cuda::std::min(a->scale(), b->scale()); + auto scale = cuda::std::min(a.scale(), b.scale()); - if (numeric::addition_overflow(a->rescaled(scale).value(), b->rescaled(scale).value())) { + if (numeric::addition_overflow(a.rescaled(scale).value(), b.rescaled(scale).value())) { return errc::OVERFLOW; } - *out = numeric::decimal{numeric::scaled_integer{ - a->rescaled(scale).value() + b->rescaled(scale).value(), numeric::scale_type{scale}}}; - return errc::SUCCESS; -} - -/** - * @brief Adds operands with overflow detection. - * - * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. - */ -template -__device__ errc ansi_add(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - if (errc e = ansi_add(&r, &a->value(), &b->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - return e; - } - *out = r; - } else { - *out = cuda::std::nullopt; - } - - return errc::SUCCESS; + return numeric::decimal{numeric::scaled_integer{ + a.rescaled(scale).value() + b.rescaled(scale).value(), numeric::scale_type{scale}}}; } /** * @brief Subtracts operands with overflow detection. * * @tparam T Value type. - * @param out Result destination. * @param a Minuend. * @param b Subtrahend. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template -__device__ errc ansi_sub(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v) +template +__device__ result ansi_sub(T a, T b) { T r; - if (cuda::sub_overflow(r, *a, *b)) { return errc::OVERFLOW; } - *out = r; - return errc::SUCCESS; + if (cuda::sub_overflow(r, a, b).overflow) { return errc::OVERFLOW; } + return r; } /** * @brief Subtracts operands with overflow detection. * * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template -__device__ errc ansi_sub(T* out, T const* a, T const* b) - requires(cuda::std::is_floating_point_v) +template +__device__ result ansi_sub(T a, T b) { - *out = *a - *b; - return errc::SUCCESS; + return a - b; } /** * @brief Subtracts operands with overflow detection. * * @tparam R Decimal representation type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ template -__device__ errc ansi_sub(numeric::decimal* out, - numeric::decimal const* a, - numeric::decimal const* b) +__device__ result> ansi_sub(numeric::decimal a, numeric::decimal b) { - auto scale = cuda::std::min(a->scale(), b->scale()); + auto scale = cuda::std::min(a.scale(), b.scale()); - if (numeric::subtraction_overflow(a->rescaled(scale).value(), b->rescaled(scale).value())) { + if (numeric::subtraction_overflow(a.rescaled(scale).value(), b.rescaled(scale).value())) { return errc::OVERFLOW; } - *out = numeric::decimal{numeric::scaled_integer{ - a->rescaled(scale).value() - b->rescaled(scale).value(), numeric::scale_type{scale}}}; - return errc::SUCCESS; -} - -/** - * @brief Subtracts operands with overflow detection. - * - * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. - */ -template -__device__ errc ansi_sub(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - if (errc e = ansi_sub(&r, &a->value(), &b->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - return e; - } - *out = r; - } else { - *out = cuda::std::nullopt; - } - return errc::SUCCESS; + return numeric::decimal{numeric::scaled_integer{ + a.rescaled(scale).value() - b.rescaled(scale).value(), numeric::scale_type{scale}}}; } /** * @brief Multiplies operands with overflow detection. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template -__device__ errc ansi_mul(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v) +template +__device__ result ansi_mul(T a, T b) { T r; - if (cuda::mul_overflow(r, *a, *b)) { return errc::OVERFLOW; } - *out = r; - return errc::SUCCESS; + if (cuda::mul_overflow(r, a, b).overflow) { return errc::OVERFLOW; } + return r; } /** * @brief Multiplies operands with overflow detection. * * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template -__device__ errc ansi_mul(T* out, T const* a, T const* b) - requires(cuda::std::is_floating_point_v) +template +__device__ result ansi_mul(T a, T b) { - *out = *a * *b; - return errc::SUCCESS; + return a * b; } /** * @brief Multiplies operands with overflow detection. * * @tparam R Decimal representation type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ template -__device__ errc ansi_mul(numeric::decimal* out, - numeric::decimal const* a, - numeric::decimal const* b) +__device__ result> ansi_mul(numeric::decimal a, numeric::decimal b) { - if (numeric::multiplication_overflow(a->value(), b->value())) { return errc::OVERFLOW; } - - *out = numeric::decimal{numeric::scaled_integer{ - a->value() * b->value(), numeric::scale_type{a->scale() + b->scale()}}}; - return errc::SUCCESS; -} + if (numeric::multiplication_overflow(a.value(), b.value())) { return errc::OVERFLOW; } -/** - * @brief Multiplies optional operands with overflow detection. - * - * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. - */ -template -__device__ errc ansi_mul(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - if (errc e = ansi_mul(&r, &a->value(), &b->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - return e; - } - *out = r; - } else { - *out = cuda::std::nullopt; - } - return errc::SUCCESS; + return numeric::decimal{ + numeric::scaled_integer{a.value() * b.value(), numeric::scale_type{a.scale() + b.scale()}}}; } /** * @brief Divides operands with ANSI checks. * * @tparam T Value type. - * @param out Result destination. * @param a Dividend. * @param b Divisor. * @return `errc::DIVISION_BY_ZERO` on zero divisor, `errc::OVERFLOW` on overflow, else * `errc::SUCCESS`. */ -template -__device__ errc ansi_div(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v) +template +__device__ result ansi_div(T a, T b) { - if (*b == 0) { return errc::DIVISION_BY_ZERO; } + if (b == 0) { return errc::DIVISION_BY_ZERO; } T r; - if (cuda::div_overflow(r, *a, *b)) { return errc::OVERFLOW; } - *out = r; - return errc::SUCCESS; + if (cuda::div_overflow(r, a, b).overflow) { return errc::OVERFLOW; } + return r; } /** @@ -278,12 +180,10 @@ __device__ errc ansi_div(T* out, T const* a, T const* b) * @tparam T Floating-point type. * @return errc::SUCCESS. */ -template -__device__ errc ansi_div(T* out, T const* a, T const* b) - requires(cuda::std::is_floating_point_v) +template +__device__ result ansi_div(T a, T b) { - *out = *a / *b; - return errc::SUCCESS; + return a / b; } /** @@ -294,196 +194,110 @@ __device__ errc ansi_div(T* out, T const* a, T const* b) * `errc::SUCCESS`. */ template -__device__ errc ansi_div(numeric::decimal* out, - numeric::decimal const* a, - numeric::decimal const* b) +__device__ result> ansi_div(numeric::decimal a, numeric::decimal b) { - if (b->value() == 0) { return errc::DIVISION_BY_ZERO; } + if (b.value() == 0) { return errc::DIVISION_BY_ZERO; } - if (numeric::division_overflow(a->value(), b->value())) { return errc::OVERFLOW; } + if (numeric::division_overflow(a.value(), b.value())) { return errc::OVERFLOW; } - *out = numeric::decimal{numeric::scaled_integer{ - a->value() / b->value(), numeric::scale_type{a->scale() - b->scale()}}}; - return errc::SUCCESS; -} - -/** - * @brief Divides operands with ANSI checks. - * - * @tparam T Value type. - * @return `errc::DIVISION_BY_ZERO` on zero divisor, `errc::OVERFLOW` on overflow, else - * `errc::SUCCESS`. - */ -template -__device__ errc ansi_div(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - if (errc e = ansi_div(&r, &a->value(), &b->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - return e; - } - *out = r; - } else { - *out = cuda::std::nullopt; - } - return errc::SUCCESS; + return numeric::decimal{ + numeric::scaled_integer{a.value() / b.value(), numeric::scale_type{a.scale() - b.scale()}}}; } /** * @brief Computes modulus with ANSI checks. * * @tparam T Value type. - * @param out Result destination. * @param a Dividend. * @param b Divisor. - * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, else the result. */ -template -__device__ errc ansi_mod(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v && cuda::std::is_signed_v) +template +__device__ result ansi_mod(T a, T b) { - if (*b == 0) { return errc::DIVISION_BY_ZERO; } + if (b == 0) { return errc::DIVISION_BY_ZERO; } // avoid signed overflow UB / trap for minimum value divided by -1. - if (*a == cuda::std::numeric_limits::min() && *b == T{-1}) { - *out = T{0}; - return errc::SUCCESS; - } + if (a == cuda::std::numeric_limits::min() && b == T{-1}) { return T{0}; } - *out = *a % *b; - return errc::SUCCESS; + return a % b; } /** * @brief Computes modulus with ANSI checks. * * @tparam T Value type. - * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. - */ -template -__device__ errc ansi_mod(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) -{ - if (*b == 0) { return errc::DIVISION_BY_ZERO; } - *out = *a % *b; - return errc::SUCCESS; -} - -/** - * @brief Computes modulus with ANSI checks. - * - * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, else the result. */ -__device__ inline errc ansi_mod(float* out, float const* a, float const* b) +template +__device__ result ansi_mod(T a, T b) { - if (*b == 0) { return errc::DIVISION_BY_ZERO; } - *out = (*a) - (*b) * ::floorf((*a) / (*b)); - return errc::SUCCESS; + if (b == 0) { return errc::DIVISION_BY_ZERO; } + return a % b; } /** * @brief Computes modulus with ANSI checks. * - * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, else the result. */ -__device__ inline errc ansi_mod(double* out, double const* a, double const* b) +template +__device__ inline result ansi_mod(T a, T b) { - if (*b == 0) { return errc::DIVISION_BY_ZERO; } - *out = (*a) - (*b) * ::floor((*a) / (*b)); - return errc::SUCCESS; + if (b == 0) { return errc::DIVISION_BY_ZERO; } + return a - b * cuda::std::floor(a / b); } /** * @brief Computes modulus with ANSI checks. * * @tparam R Decimal representation type. - * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. + * @return `errc::DIVISION_BY_ZERO` on zero divisor, else the result. */ template -__device__ errc ansi_mod(numeric::decimal* out, - numeric::decimal const* a, - numeric::decimal const* b) -{ - numeric::decimal div; - - if (errc e = ansi_div(&div, a, b); e != errc::SUCCESS) { return e; } - - numeric::decimal quotient; - floor("ient, &div); - *out = *a - *b * quotient; - return errc::SUCCESS; -} - -/** - * @brief Computes modulus with ANSI checks. - * - * @tparam T Value type. - * @return `errc::DIVISION_BY_ZERO` on zero divisor, else `errc::SUCCESS`. - */ -template -__device__ errc ansi_mod(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +__device__ result> ansi_mod(numeric::decimal a, numeric::decimal b) { - if (a->has_value() && b->has_value()) { - T r; - if (errc e = ansi_mod(&r, &a->value(), &b->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - return e; - } - *out = r; - } else { - *out = cuda::std::nullopt; - } - return errc::SUCCESS; + auto r = ansi_div(a, b); + if (r.has_error()) { return r.error(); } + return a - b * floor(r.value()); } /** * @brief Computes absolute value with ANSI overflow checks. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template -__device__ errc ansi_abs(T* out, T const* a) - requires(cuda::std::is_integral_v && cuda::std::is_signed_v) +template +__device__ result ansi_abs(T a) { - if (*a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } - *out = (*a < 0) ? -(*a) : *a; - return errc::SUCCESS; + if (a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + return (a < 0) ? -a : a; } /** * @brief Computes absolute value with ANSI overflow checks. * * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template -__device__ errc ansi_abs(T* out, T const* a) - requires(cuda::std::is_integral_v && cuda::std::is_unsigned_v) +template +__device__ result ansi_abs(T a) { - *out = *a; - return errc::SUCCESS; + return a; } /** * @brief Computes absolute value with ANSI overflow checks. * * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template - requires(cuda::std::is_floating_point_v) -__device__ errc ansi_abs(T* out, T const* a) +template +__device__ result ansi_abs(T a) { - *out = cuda::std::fabs(*a); - return errc::SUCCESS; + return cuda::std::fabs(a); } /** @@ -493,356 +307,78 @@ __device__ errc ansi_abs(T* out, T const* a) * @return `errc::OVERFLOW` on overflow, else errc::SUCCESS. */ template -__device__ errc ansi_abs(numeric::decimal* out, numeric::decimal const* a) -{ - if (a->value() == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } - auto rep = a->value() < 0 ? -a->value() : a->value(); - *out = numeric::decimal{numeric::scaled_integer{rep, numeric::scale_type{a->scale()}}}; - return errc::SUCCESS; -} - -/** - * @brief Computes absolute value for with ANSI overflow checks. - * - * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. - */ -template -__device__ errc ansi_abs(cuda::std::optional* out, cuda::std::optional const* a) +__device__ result> ansi_abs(numeric::decimal a) { - if (a->has_value()) { - T r; - if (errc e = ansi_abs(&r, &a->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - return e; - } - *out = r; - } else { - *out = cuda::std::nullopt; - } - return errc::SUCCESS; + if (a.value() == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + auto rep = a.value() < 0 ? -a.value() : a.value(); + return numeric::decimal{numeric::scaled_integer{rep, numeric::scale_type{a.scale()}}}; } /** * @brief Computes unary negation with ANSI overflow checks. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template -__device__ errc ansi_neg(T* out, T const* a) - requires(cuda::std::is_integral_v && cuda::std::is_signed_v) +template +__device__ result ansi_neg(T a) { - if (*a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } - *out = -(*a); - return errc::SUCCESS; + if (a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + return -a; } /** * @brief Computes unary negation with ANSI overflow checks. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ -template -__device__ errc ansi_neg(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ result ansi_neg(T a) { - *out = -(*a); - return errc::SUCCESS; + return -a; } /** * @brief Computes unary negation with ANSI overflow checks. * * @tparam R Decimal representation type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` on overflow, else the result. */ template -__device__ errc ansi_neg(numeric::decimal* out, numeric::decimal const* a) -{ - if (a->value() == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } - auto rep = -a->value(); - *out = numeric::decimal{numeric::scaled_integer{rep, numeric::scale_type{a->scale()}}}; - return errc::SUCCESS; -} - -/** - * @brief Computes unary negation with ANSI overflow checks. - * - * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else `errc::SUCCESS`. - */ -template -__device__ errc ansi_neg(cuda::std::optional* out, cuda::std::optional const* a) +__device__ result> ansi_neg(numeric::decimal a) { - if (a->has_value()) { - T r; - if (errc e = ansi_neg(&r, &a->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - return e; - } - *out = r; - } else { - *out = cuda::std::nullopt; - } - return errc::SUCCESS; + if (a.value() == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + auto rep = -a.value(); + return numeric::decimal{numeric::scaled_integer{rep, numeric::scale_type{a.scale()}}}; } /** * @brief Validates decimal precision against a target precision value. * * @tparam R Decimal representation type. - * @param out Result destination. * @param a Input decimal value. * @param precision Maximum allowed precision. - * @return `errc::OVERFLOW` when precision is invalid or exceeded, else `errc::SUCCESS`. + * @return `errc::OVERFLOW` when precision is invalid or exceeded, else the result. */ template -__device__ errc ansi_precision_check(numeric::decimal* out, - numeric::decimal const* a, - int32_t const* precision) +__device__ result> ansi_precision_check(numeric::decimal a, + int32_t precision) { - if (*precision <= 0) { return errc::OVERFLOW; } + if (precision <= 0) { return errc::OVERFLOW; } - auto value = a->value(); + auto value = a.value(); if (value == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } auto abs_value = value < 0 ? -value : value; - if (abs_value >= numeric::detail::ipow(*precision)) { + if (abs_value >= numeric::detail::ipow(precision)) { return errc::OVERFLOW; } - *out = *a; - return errc::SUCCESS; -} - -/** - * @brief Validates decimal precision against a target precision value. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Decimal input. - * @param precision Precision. - * @return `errc::OVERFLOW` when precision is invalid or exceeded, else `errc::SUCCESS`. - */ -template -__device__ errc ansi_precision_check(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* precision) -{ - if (a->has_value() && precision->has_value()) { - T r; - if (errc e = ansi_precision_check(&r, &a->value(), &precision->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - return e; - } else { - *out = r; - return errc::SUCCESS; - } - } else { - *out = cuda::std::nullopt; - return errc::SUCCESS; - } -} - -/** - * @brief ANSI add that returns null instead of propagating errors. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void ansi_try_add(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - if (errc e = ansi_add(&r, &a->value(), &b->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - } else { - *out = r; - } - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief ANSI subtract that returns null instead of propagating errors. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Minuend. - * @param b Subtrahend. - */ -template -__device__ void ansi_try_sub(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - if (errc e = ansi_sub(&r, &a->value(), &b->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - } else { - *out = r; - } - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief ANSI multiply that returns null instead of propagating errors. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void ansi_try_mul(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - if (errc e = ansi_mul(&r, &a->value(), &b->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - } else { - *out = r; - } - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief ANSI divide that returns null instead of propagating errors. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Dividend. - * @param b Divisor. - */ -template -__device__ void ansi_try_div(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - if (errc e = ansi_div(&r, &a->value(), &b->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - } else { - *out = r; - } - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief ANSI modulus that returns null instead of propagating errors. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Dividend. - * @param b Divisor. - */ -template -__device__ void ansi_try_mod(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - if (errc e = ansi_mod(&r, &a->value(), &b->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - } else { - *out = r; - } - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief ANSI absolute value that returns null instead of propagating errors. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void ansi_try_abs(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - if (errc e = ansi_abs(&r, &a->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - } else { - *out = r; - } - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief ANSI unary negation that returns null instead of propagating errors. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void ansi_try_neg(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - if (errc e = ansi_neg(&r, &a->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - } else { - *out = r; - } - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief ANSI precision check that returns null instead of propagating errors. - * - * @tparam R Decimal representation type. - * @param out Result destination. - * @param a Decimal input. - * @param precision Precision. - */ -template -__device__ void ansi_try_precision_check(cuda::std::optional>* out, - cuda::std::optional> const* a, - cuda::std::optional const* precision) -{ - if (a->has_value() && precision->has_value()) { - numeric::decimal r; - if (errc e = ansi_precision_check(&r, &a->value(), &precision->value()); e != errc::SUCCESS) { - *out = cuda::std::nullopt; - } else { - *out = r; - } - } else { - *out = cuda::std::nullopt; - } + return a; } } // namespace ops diff --git a/cpp/include/cudf/operators/arithmetic.cuh b/cpp/include/cudf/operators/arithmetic.cuh index 283f4ae763ce..a4c7de5a7b2f 100644 --- a/cpp/include/cudf/operators/arithmetic.cuh +++ b/cpp/include/cudf/operators/arithmetic.cuh @@ -6,10 +6,12 @@ #include #include +#include +#include #include #include -#include +#include #include namespace CUDF_EXPORT cudf { @@ -19,429 +21,210 @@ namespace ops { * @brief Computes absolute value. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. */ template -__device__ void abs(T* out, T const* a) - requires(cuda::std::is_integral_v || cuda::std::is_floating_point_v) +__device__ T abs(T a) + requires(cuda::std::is_signed_v) { - *out = cuda::std::abs(*a); + return cuda::std::abs(a); } /** * @brief Computes absolute value. * - * @tparam R Decimal representation type. + * @tparam T Value type. + * @param a Input value. */ -template -__device__ void abs(numeric::decimal* out, numeric::decimal const* a) +template +__device__ T abs(T a) { - auto rep = a->value() < 0 ? -a->value() : a->value(); - *out = numeric::decimal{numeric::scaled_integer{rep, a->scale()}}; + return a; } /** * @brief Computes absolute value. * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. + * @tparam R Decimal representation type. */ -template -__device__ void abs(cuda::std::optional* out, cuda::std::optional const* a) +template +__device__ numeric::decimal abs(numeric::decimal a) { - if (a->has_value()) { - T r; - abs(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + auto rep = a.value() < 0 ? -a.value() : a.value(); + return numeric::decimal{numeric::scaled_integer{rep, a.scale()}}; } /** * @brief Computes sum of two values. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void add(T* out, T const* a, T const* b) +template +__device__ T add(T a, T b) { - *out = (*a + *b); -} - -/** - * @brief Computes sum of two values. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left optional operand. - * @param b Right optional operand. - */ -template -__device__ void add(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - add(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return (a + b); } /** * @brief Computes quotient of two values. * * @tparam T Value type. - * @param out Result destination. * @param a Dividend. * @param b Divisor. */ -template -__device__ void div(T* out, T const* a, T const* b) +template +__device__ T div(T a, T b) { - *out = (*a / *b); -} - -/** - * @brief Computes quotient of two values. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Dividend. - * @param b Divisor. - */ -template -__device__ void div(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - div(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes floor division of two values. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Dividend. - * @param b Divisor. - */ -template -__device__ void floor_div(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v) -{ - *out = cudf::detail::integral_floor_div(*a, *b); + return (a / b); } /** * @brief Computes floor division of two values. * * @tparam T Value type. - * @param out Result destination. * @param a Dividend. * @param b Divisor. */ -template -__device__ inline void floor_div(T* out, T const* a, T const* b) - requires(cuda::std::is_floating_point_v) +template +__device__ T floor_div(T a, T b) { - *out = cuda::std::floor(*a / *b); + return cudf::detail::integral_floor_div(a, b); } /** * @brief Computes floor division of two values. * * @tparam T Value type. - * @param out Result destination. - * @param a Dividend. - * @param b Divisor. - */ -template -__device__ void floor_div(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - floor_div(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes remainder of two values. - * - * @tparam T Value type. - * @param out Result destination. * @param a Dividend. * @param b Divisor. */ -template -__device__ void mod(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v || cudf::is_fixed_point()) +template +__device__ T floor_div(T a, T b) { - *out = (*a % *b); + return cuda::std::floor(a / b); } /** * @brief Computes remainder of two values. * * @tparam T Value type. - * @param out Result destination. * @param a Dividend. * @param b Divisor. */ template -__device__ inline void mod(T* out, T const* a, T const* b) - requires(cuda::std::is_floating_point_v) +__device__ T mod(T a, T b) + requires(integer || fixed_point) { - *out = cuda::std::fmod(*a, *b); + return (a % b); } /** * @brief Computes remainder of two values. * * @tparam T Value type. - * @param out Result destination. - * @param a Dividend. - * @param b Divisor. - */ -template -__device__ void mod(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - mod(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes Python-style modulus. - * - * @tparam T Value type. - * @param out Result destination. * @param a Dividend. * @param b Divisor. */ template -__device__ void pymod(T* out, T const* a, T const* b) - requires(cuda::std::is_integral_v || cudf::is_fixed_point()) +__device__ T mod(T a, T b) + requires(floating_point) { - *out = (*a % *b + *b) % *b; + return cuda::std::fmod(a, b); } /** * @brief Computes Python-style modulus. * * @tparam T Value type. - * @param out Result destination. * @param a Dividend. * @param b Divisor. */ template -__device__ inline void pymod(T* out, T const* a, T const* b) - requires(cuda::std::is_floating_point_v) +__device__ T pymod(T a, T b) + requires(integer || fixed_point) { - *out = cuda::std::fmod(cuda::std::fmod(*a, *b) + *b, *b); + return (a % b + b) % b; } /** * @brief Computes Python-style modulus. * * @tparam T Value type. - * @param out Result destination. * @param a Dividend. * @param b Divisor. */ -template -__device__ void pymod(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +template +__device__ T pymod(T a, T b) { - if (a->has_value() && b->has_value()) { - T r; - pymod(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::fmod(cuda::std::fmod(a, b) + b, b); } /** * @brief Computes product of two values. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void mul(T* out, T const* a, T const* b) +template +__device__ T mul(T a, T b) { - *out = (*a * *b); -} - -/** - * @brief Computes product of two values. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void mul(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - mul(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return (a * b); } /** * @brief Computes unary negation. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. */ template -__device__ void neg(T* out, T const* a) - requires(cuda::std::is_signed_v) +__device__ T neg(T a) + requires(signed_integer || floating_point) { - *out = -(*a); + return -a; } /** * @brief Computes unary negation. * * @tparam R Decimal representation type. - * @param out Result destination. * @param a Input value. */ template -__device__ void neg(numeric::decimal* out, numeric::decimal const* a) +__device__ numeric::decimal neg(numeric::decimal a) { - auto rep = -a->value(); - *out = numeric::decimal{numeric::scaled_integer{rep, a->scale()}}; -} - -/** - * @brief Computes unary negation. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void neg(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - neg(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes subtraction of two values. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Minuend. - * @param b Subtrahend. - */ -template -__device__ void sub(T* out, T const* a, T const* b) -{ - *out = *a - *b; + auto rep = -a.value(); + return numeric::decimal{numeric::scaled_integer{rep, a.scale()}}; } /** * @brief Computes subtraction of two values. * * @tparam T Value type. - * @param out Result destination. * @param a Minuend. * @param b Subtrahend. */ -template -__device__ void sub(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - sub(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes true division and returns a double. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Dividend. - * @param b Divisor. - */ -template -__device__ void true_div(double* out, T const* a, T const* b) - requires(cuda::std::is_floating_point_v || cuda::std::is_integral_v) +template +__device__ T sub(T a, T b) { - *out = static_cast(*a) / static_cast(*b); + return a - b; } /** * @brief Computes true division and returns a double. * * @tparam T Value type. - * @param out Result destination. * @param a Dividend. * @param b Divisor. */ template -__device__ void true_div(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +__device__ double true_div(T a, T b) + requires(floating_point || integer) { - if (a->has_value() && b->has_value()) { - double r; - true_div(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return static_cast(a) / static_cast(b); } } // namespace ops diff --git a/cpp/include/cudf/operators/bitwise.cuh b/cpp/include/cudf/operators/bitwise.cuh index c6b4b5d2bbdb..ef9d5abfe7f7 100644 --- a/cpp/include/cudf/operators/bitwise.cuh +++ b/cpp/include/cudf/operators/bitwise.cuh @@ -4,10 +4,10 @@ */ #pragma once +#include +#include #include -#include - namespace CUDF_EXPORT cudf { namespace ops { @@ -15,212 +15,77 @@ namespace ops { * @brief Computes bitwise AND of two values. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void bit_and(T* out, T const* a, T const* b) -{ - *out = (*a & *b); -} - -/** - * @brief Computes bitwise AND of two values. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void bit_and(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - bit_and(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes bitwise NOT of one value. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void bit_invert(T* out, T const* a) +template +__device__ T bit_and(T a, T b) { - *out = ~(*a); + return (a & b); } /** * @brief Computes bitwise NOT of one value. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. */ -template -__device__ void bit_invert(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - bit_invert(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes bitwise OR of two values. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void bit_or(T* out, T const* a, T const* b) +template +__device__ T bit_invert(T a) { - *out = (*a | *b); + return ~a; } /** * @brief Computes bitwise OR of two values. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void bit_or(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +template +__device__ T bit_or(T a, T b) { - if (a->has_value() && b->has_value()) { - T r; - bit_or(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return (a | b); } /** * @brief Computes bitwise XOR of two values. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void bit_xor(T* out, T const* a, T const* b) +template +__device__ T bit_xor(T a, T b) { - *out = (*a ^ *b); -} - -/** - * @brief Computes bitwise XOR of two values. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void bit_xor(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - bit_xor(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Shifts a value left by a bit count. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - * @param b Shift count. - */ -template -__device__ void bit_shift_left(T* out, T const* a, T const* b) -{ - *out = (*a << *b); + return (a ^ b); } /** * @brief Shifts a value left by a bit count. * * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - * @param b Shift count. - */ -template -__device__ void bit_shift_left(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - T r; - bit_shift_left(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Shifts a value right by a bit count. - * - * @tparam T Value type. - * @param out Result destination. * @param a Input value. * @param b Shift count. */ -template -__device__ void bit_shift_right(T* out, T const* a, T const* b) +template +__device__ T bit_shift_left(T a, T b) { - *out = (*a >> *b); + return (a << b); } /** * @brief Shifts a value right by a bit count. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. * @param b Shift count. */ -template -__device__ void bit_shift_right(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +template +__device__ T bit_shift_right(T a, T b) { - if (a->has_value() && b->has_value()) { - T r; - bit_shift_right(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return (a >> b); } } // namespace ops diff --git a/cpp/include/cudf/operators/casts.cuh b/cpp/include/cudf/operators/casts.cuh index 9dac2e488094..30d87e127007 100644 --- a/cpp/include/cudf/operators/casts.cuh +++ b/cpp/include/cudf/operators/casts.cuh @@ -6,9 +6,10 @@ #include #include +#include +#include #include -#include #include namespace CUDF_EXPORT cudf { @@ -18,380 +19,167 @@ namespace ops { * @brief Casts input values to bool. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_b8(bool* out, T const* a) +__device__ bool cast_to_b8(T a) + requires(!nullable && cuda::std::convertible_to) { - *out = static_cast(*a); -} - -/** - * @brief Casts input values to bool. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_b8(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - bool r; - cast_to_b8(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Casts input values to int8_t. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_i8(int8_t* out, T const* a) -{ - *out = static_cast(*a); + return static_cast(a); } /** * @brief Casts input values to int8_t. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_i8(cuda::std::optional* out, cuda::std::optional const* a) +__device__ int8_t cast_to_i8(T a) + requires(!nullable && cuda::std::convertible_to) { - if (a->has_value()) { - int8_t r; - cast_to_i8(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return static_cast(a); } /** * @brief Casts input values to int16_t. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_i16(int16_t* out, T const* a) +__device__ int16_t cast_to_i16(T a) + requires(!nullable && cuda::std::convertible_to) { - *out = static_cast(*a); -} - -/** - * @brief Casts input values to int16_t. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_i16(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - int16_t r; - cast_to_i16(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return static_cast(a); } /** * @brief Casts input values to int32_t. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_i32(int32_t* out, T const* a) +__device__ int32_t cast_to_i32(T a) + requires(!nullable && cuda::std::convertible_to) { - *out = static_cast(*a); -} - -/** - * @brief Casts input values to int32_t. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_i32(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - int32_t r; - cast_to_i32(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return static_cast(a); } /** * @brief Casts input values to int64_t. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_i64(int64_t* out, T const* a) +__device__ int64_t cast_to_i64(T a) + requires(!nullable && cuda::std::convertible_to) { - *out = static_cast(*a); -} - -/** - * @brief Casts input values to int64_t. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_i64(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - int64_t r; - cast_to_i64(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return static_cast(a); } /** * @brief Casts input values to uint8_t. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_u8(uint8_t* out, T const* a) +__device__ uint8_t cast_to_u8(T a) + requires(!nullable && cuda::std::convertible_to) { - *out = static_cast(*a); -} - -/** - * @brief Casts input values to uint8_t. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_u8(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - uint8_t r; - cast_to_u8(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Casts input values to uint16_t. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_u16(uint16_t* out, T const* a) -{ - *out = static_cast(*a); + return static_cast(a); } /** * @brief Casts input values to uint16_t. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_u16(cuda::std::optional* out, cuda::std::optional const* a) +__device__ uint16_t cast_to_u16(T a) + requires(!nullable && cuda::std::convertible_to) { - if (a->has_value()) { - uint16_t r; - cast_to_u16(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return static_cast(a); } /** * @brief Casts input values to uint32_t. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_u32(uint32_t* out, T const* a) +__device__ uint32_t cast_to_u32(T a) + requires(!nullable && cuda::std::convertible_to) { - *out = static_cast(*a); -} - -/** - * @brief Casts input values to uint32_t. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_u32(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - uint32_t r; - cast_to_u32(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return static_cast(a); } /** * @brief Casts input values to uint64_t. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_u64(uint64_t* out, T const* a) +__device__ uint64_t cast_to_u64(T a) + requires(!nullable && cuda::std::convertible_to) { - *out = static_cast(*a); -} - -/** - * @brief Casts input values to uint64_t. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_u64(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - uint64_t r; - cast_to_u64(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return static_cast(a); } /** * @brief Casts input values to float. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_f32(float* out, T const* a) +__device__ float cast_to_f32(T a) requires(cuda::std::is_integral_v || cuda::std::is_floating_point_v) { - *out = static_cast(*a); + return static_cast(a); } /** * @brief Casts fixed-point decimal values to float. * * @tparam R Source decimal representation type. - * @param out Result destination. * @param a Source decimal value. */ template -__device__ void cast_to_f32(float* out, numeric::decimal const* a) +__device__ float cast_to_f32(numeric::decimal a) { - *out = convert_fixed_to_floating(*a); -} - -/** - * @brief Casts input values to float. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Optional input value. - */ -template -__device__ void cast_to_f32(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - float r; - cast_to_f32(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return convert_fixed_to_floating(a); } /** * @brief Casts input values to double. * * @tparam T Source type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_f64(double* out, T const* a) - requires(cuda::std::is_integral_v || cuda::std::is_floating_point_v) +__device__ double cast_to_f64(T a) + requires(cuda::std::is_integral_v || floating_point || fixed_point) { - *out = static_cast(*a); + return static_cast(a); } /** * @brief Casts input values to double. * * @tparam R Source decimal representation type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_f64(double* out, numeric::decimal const* a) -{ - *out = convert_fixed_to_floating(*a); -} - -/** - * @brief Casts optional input values to optional double. - * - * @tparam T Source type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_f64(cuda::std::optional* out, cuda::std::optional const* a) +__device__ double cast_to_f64(numeric::decimal a) { - if (a->has_value()) { - double r; - cast_to_f64(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return convert_fixed_to_floating(a); } namespace detail { @@ -401,14 +189,13 @@ namespace detail { * * @tparam To Destination representation type. * @tparam From Source representation type. - * @param out Result destination. * @param a Input value. */ template -__device__ void decimal_cast(numeric::decimal* out, numeric::decimal const* a) +__device__ numeric::decimal decimal_cast(numeric::decimal a) { - auto rep = static_cast(a->value()); - *out = numeric::decimal{numeric::scaled_integer{rep, a->scale()}}; + auto rep = static_cast(a.value()); + return numeric::decimal{numeric::scaled_integer{rep, a.scale()}}; } } // namespace detail @@ -417,137 +204,49 @@ __device__ void decimal_cast(numeric::decimal* out, numeric::decimal c * @brief Casts decimal input values to decimal32. * * @tparam R Source decimal representation type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_dec32(numeric::decimal32* out, numeric::decimal const* a) -{ - return detail::decimal_cast(out, a); -} - -/** - * @brief Casts decimal input values to decimal32. - * - * @tparam R Source decimal representation type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_dec32(cuda::std::optional* out, - cuda::std::optional> const* a) +__device__ numeric::decimal32 cast_to_dec32(numeric::decimal a) { - if (a->has_value()) { - numeric::decimal32 r; - cast_to_dec32(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return detail::decimal_cast(a); } /** * @brief Casts decimal input values to decimal64. * * @tparam R Source decimal representation type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_dec64(numeric::decimal64* out, numeric::decimal const* a) +__device__ numeric::decimal64 cast_to_dec64(numeric::decimal a) { - return detail::decimal_cast(out, a); -} - -/** - * @brief Casts optional decimal input values to optional decimal64. - * - * @tparam R Source decimal representation type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_dec64(cuda::std::optional* out, - cuda::std::optional> const* a) -{ - if (a->has_value()) { - numeric::decimal64 r; - cast_to_dec64(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return detail::decimal_cast(a); } /** * @brief Casts decimal input values to decimal128. * * @tparam R Source decimal representation type. - * @param out Result destination. * @param a Input value. */ template -__device__ void cast_to_dec128(numeric::decimal128* out, numeric::decimal const* a) -{ - return detail::decimal_cast(out, a); -} - -/** - * @brief Casts decimal input values to decimal128. - * - * @tparam R Source decimal representation type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cast_to_dec128(cuda::std::optional* out, - cuda::std::optional> const* a) -{ - if (a->has_value()) { - numeric::decimal128 r; - cast_to_dec128(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Rescales decimal input values to a target scale. - * - * @tparam R Decimal representation type. - * @param out Result destination. - * @param a Input value. - * @param new_scale Target decimal scale. - */ -template -__device__ void rescale(numeric::decimal* out, - numeric::decimal const* a, - int32_t const* new_scale) +__device__ numeric::decimal128 cast_to_dec128(numeric::decimal a) { - *out = a->rescaled(numeric::scale_type{*new_scale}); + return detail::decimal_cast(a); } /** * @brief Rescales decimal input values to a target scale. * * @tparam R Decimal representation type. - * @param out Result destination. * @param a Input value. * @param new_scale Target decimal scale. */ template -__device__ void rescale(cuda::std::optional>* out, - cuda::std::optional> const* a, - cuda::std::optional const* new_scale) +__device__ numeric::decimal rescale(numeric::decimal a, int32_t new_scale) { - if (a->has_value() && new_scale->has_value()) { - numeric::decimal r; - rescale(&r, &a->value(), &new_scale->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return a.rescaled(numeric::scale_type{new_scale}); } } // namespace ops diff --git a/cpp/include/cudf/operators/comparison.cuh b/cpp/include/cudf/operators/comparison.cuh index facb0c3726d5..bed06e38add0 100644 --- a/cpp/include/cudf/operators/comparison.cuh +++ b/cpp/include/cudf/operators/comparison.cuh @@ -4,6 +4,8 @@ */ #pragma once +#include +#include #include #include @@ -15,253 +17,117 @@ namespace ops { * @brief Tests `a == b`. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ template -__device__ void equal(bool* out, T const* a, T const* b) +__device__ bool equal(T a, T b) + requires(!nullable && cuda::std::equality_comparable) { - *out = (*a == *b); -} - -/** - * @brief Tests `a == b`. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void equal(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - bool r; - equal(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return (a == b); } /** * @brief Tests `a != b`. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ template -__device__ void not_equal(bool* out, T const* a, T const* b) +__device__ bool not_equal(T a, T b) + requires(!nullable && cuda::std::equality_comparable) { - *out = (*a != *b); -} - -/** - * @brief Tests `a != b`. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void not_equal(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - bool r; - not_equal(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return (a != b); } /** * @brief Tests `a > b`. * * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void greater(bool* out, T const* a, T const* b) -{ - *out = (*a > *b); -} - -/** - * @brief Tests `a > b`. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void greater(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - bool r; - greater(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Tests `a >= b`. - * - * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ template -__device__ void greater_equal(bool* out, T const* a, T const* b) +__device__ bool greater(T a, T b) + requires(!nullable && cuda::std::totally_ordered) { - *out = (*a >= *b); + return (a > b); } /** * @brief Tests `a >= b`. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ template -__device__ void greater_equal(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +__device__ bool greater_equal(T a, T b) + requires(!nullable && cuda::std::totally_ordered) { - if (a->has_value() && b->has_value()) { - bool r; - greater_equal(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return (a >= b); } /** * @brief Tests `a < b`. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ template -__device__ void less(bool* out, T const* a, T const* b) +__device__ bool less(T a, T b) + requires(!nullable && cuda::std::totally_ordered) { - *out = (*a < *b); -} - -/** - * @brief Tests `a < b`. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void less(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - bool r; - less(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return (a < b); } /** * @brief Tests `a <= b`. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ template -__device__ void less_equal(bool* out, T const* a, T const* b) +__device__ bool less_equal(T a, T b) + requires(!nullable && cuda::std::totally_ordered) { - *out = (*a <= *b); -} - -/** - * @brief Tests `a <= b`. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void less_equal(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - bool r; - less_equal(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return (a <= b); } /** * @brief Tests equality between two values for null-aware equality semantics. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ template -__device__ void null_equal(bool* out, T const* a, T const* b) +__device__ bool null_equal(T a, T b) + requires(!nullable && cuda::std::equality_comparable) { - *out = (*a == *b); + return (a == b); } /** * @brief Tests equality between two values for null-aware equality semantics. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ template -__device__ void null_equal(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +__device__ bool null_equal(optional a, optional b) + requires(!nullable && cuda::std::equality_comparable) { - if (a->has_value() && b->has_value()) { - bool r; - null_equal(&r, &a->value(), &b->value()); - *out = r; - } else if (!a->has_value() && !b->has_value()) { - *out = true; + if (a.has_value() && b.has_value()) { + return null_equal(a.value(), b.value()); + } else if (!a.has_value() && !b.has_value()) { + return true; } else { - *out = false; + return false; } } diff --git a/cpp/include/cudf/operators/concepts.cuh b/cpp/include/cudf/operators/concepts.cuh new file mode 100644 index 000000000000..093463c43991 --- /dev/null +++ b/cpp/include/cudf/operators/concepts.cuh @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +template +concept arithmetic = + (cuda::std::is_arithmetic_v && !cuda::std::is_same_v, bool>) || + cudf::is_fixed_point(); + +template +concept integral_or_floating_point_not_bool = + (cuda::std::is_integral_v || cuda::std::is_floating_point_v) && + !cuda::std::is_same_v, bool>; + +template +concept integer = + cuda::std::is_integral_v && !cuda::std::is_same_v, bool>; + +template +concept signed_integer = integer && cuda::std::is_signed_v>; + +template +concept unsigned_integer = integer && cuda::std::is_unsigned_v>; + +template +concept fixed_point = cudf::is_fixed_point(); + +template +concept floating_point = cuda::std::is_floating_point_v; + +template +constexpr bool is_nullable = false; + +template +constexpr bool is_nullable> = true; + +template +concept nullable = is_nullable; + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/error.hpp b/cpp/include/cudf/operators/error.hpp index c087406bbcf1..d50b0c763943 100644 --- a/cpp/include/cudf/operators/error.hpp +++ b/cpp/include/cudf/operators/error.hpp @@ -7,11 +7,98 @@ #include #include +#include namespace CUDF_EXPORT cudf { namespace ops { +/** + * @brief An enumeration of error codes that can occur during operations. + */ enum class errc : cuda::std::int8_t { SUCCESS = 0, OVERFLOW = 1, DIVISION_BY_ZERO = 2 }; +/** + * @brief A type that represents the result of an operation, which can either be a value or an + * error. + * + * @tparam T The type of the value. + */ +template +struct result { + private: + errc error_; //< The error code of the result, errc::SUCCESS if the operation was successful + T value_; //< The value of the result, only valid if error_ is errc::SUCCESS + + public: + /** + * @brief Constructs a result with a value. + * + * @param value The value of the result. + */ + __device__ constexpr result(T value) : error_(errc::SUCCESS), value_(value) {} + + /** + * @brief Constructs a result with an error. + * + * @param error The error code of the result. + */ + __device__ constexpr result(errc error) : error_(error), value_() {} + + /** + * @brief Checks if the result has an error. + * + * @return true if the result has an error, false otherwise. + */ + [[nodiscard]] __device__ __host__ constexpr bool has_error() const noexcept + { + return error_ != errc::SUCCESS; + } + + /** + * @brief Checks if the result has a value. + * + * @return true if the result has a value, false otherwise. + */ + [[nodiscard]] __device__ __host__ constexpr bool has_value() const noexcept + { + return !has_error(); + } + + /** + * @brief Returns true if the result has a value, false otherwise. This operator allows the result + * to be used in boolean contexts, such as if statements. + * + * @return true if the result has a value, false otherwise. + */ + [[nodiscard]] __device__ __host__ constexpr explicit operator bool() const noexcept + { + return has_value(); + } + + /** + * @brief Returns the value of the result. Behaviour is undefined if the result has an error (i.e. + * value() is called on a result that has an error). + * + * @return The value of the result. + */ + __device__ __host__ constexpr T const& value() const { return value_; } + + /** + * @brief Returns the error code of the result. Behaviour is undefined if the result has a value + * (i.e. error() is called on a result that does not have an error). + * + * @return The error code of the result. + */ + [[nodiscard]] __device__ __host__ constexpr errc error() const { return error_; } +}; + +// Helper variable template to detect if a type is a result type +template +constexpr bool is_result = false; + +// Specialization for result types +template +constexpr bool is_result> = true; + } // namespace ops } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/identity.cuh b/cpp/include/cudf/operators/identity.cuh index 67b21eb1c043..0a8b4b72f2a5 100644 --- a/cpp/include/cudf/operators/identity.cuh +++ b/cpp/include/cudf/operators/identity.cuh @@ -6,8 +6,6 @@ #include -#include - namespace CUDF_EXPORT cudf { namespace ops { @@ -15,13 +13,12 @@ namespace ops { * @brief Copies an input value to the output. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. */ template -__device__ void identity(T* out, T const* a) +__device__ T identity(T a) { - *out = *a; + return a; } } // namespace ops diff --git a/cpp/include/cudf/operators/logic.cuh b/cpp/include/cudf/operators/logic.cuh index 2ca146e3ab83..807d02a4406a 100644 --- a/cpp/include/cudf/operators/logic.cuh +++ b/cpp/include/cudf/operators/logic.cuh @@ -4,6 +4,8 @@ */ #pragma once +#include +#include #include #include @@ -15,40 +17,34 @@ namespace ops { * @brief Computes logical AND with null-aware semantics. * * @tparam T Operand type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void null_logical_and(bool* out, T const* a, T const* b) +template +__device__ bool null_logical_and(T a, T b) { - *out = (*a && *b); + return a && b; } /** * @brief Computes logical AND with null-aware semantics. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void null_logical_and(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +template +__device__ optional null_logical_and(optional a, optional b) { - if (a->has_value() && b->has_value()) { - bool r; - null_logical_and(&r, &a->value(), &b->value()); - *out = r; - } else if (!a->has_value() && !b->has_value()) { - *out = cuda::std::nullopt; + if (a.has_value() && b.has_value()) { + return null_logical_and(a.value(), b.value()); + } else if (!a.has_value() && !b.has_value()) { + return {}; } else { - if (a->has_value() ? *(*a) : *(*b)) { - *out = cuda::std::nullopt; + if (a.has_value() ? *a : *b) { + return {}; } else { - *out = false; + return false; } } } @@ -57,179 +53,102 @@ __device__ void null_logical_and(cuda::std::optional* out, * @brief Computes logical OR with null-aware semantics. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void null_logical_or(bool* out, T const* a, T const* b) +template +__device__ bool null_logical_or(T a, T b) { - *out = (*a || *b); + return a || b; } /** * @brief Computes logical OR with null-aware semantics. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void null_logical_or(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +template +__device__ optional null_logical_or(optional a, optional b) { - if (a->has_value() && b->has_value()) { - bool r; - null_logical_or(&r, &a->value(), &b->value()); - *out = r; - } else if (!a->has_value() && !b->has_value()) { - *out = cuda::std::nullopt; + if (a.has_value() && b.has_value()) { + return null_logical_or(a.value(), b.value()); + } else if (!a.has_value() && !b.has_value()) { + return {}; } else { - if (a->has_value() ? *(*a) : *(*b)) { - *out = true; + if (a.has_value() ? *a : *b) { + return true; } else { - *out = cuda::std::nullopt; + return {}; } } } /** - * @brief Computes logical AND with null-aware semantics. + * @brief Computes logical AND. * * @tparam T Operand type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void logical_and(bool* out, T const* a, T const* b) -{ - *out = (*a && *b); -} - -/** - * @brief Computes logical AND with null-aware semantics. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ void logical_and(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) -{ - if (a->has_value() && b->has_value()) { - bool r; - logical_and(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes logical OR with null-aware semantics. - * - * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void logical_or(bool* out, T const* a, T const* b) +template +__device__ bool logical_and(T a, T b) { - *out = (*a || *b); + return a && b; } /** - * @brief Computes logical OR with null-aware semantics. + * @brief Computes logical OR. * * @tparam T Value type. - * @param out Result destination. * @param a Left operand. * @param b Right operand. */ -template -__device__ void logical_or(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +template +__device__ bool logical_or(T a, T b) { - if (a->has_value() && b->has_value()) { - bool r; - logical_or(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return a || b; } /** - * @brief Computes logical NOT with null-aware semantics. + * @brief Computes logical NOT. * * @tparam T Value type. - * @param out Result destination. * @param a Input operand. */ -template -__device__ void logical_not(bool* out, T const* a) +template +__device__ bool logical_not(T a) { - *out = !(*a); -} - -/** - * @brief Computes logical NOT with null-aware semantics. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input operand. - */ -template -__device__ void logical_not(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - bool r; - logical_not(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return !a; } /** * @brief Selects one of two values based on a predicate. * * @tparam T Selected value type. - * @param out Result destination. * @param true_value Value selected when @p pred is true. * @param false_value Value selected when @p pred is false. * @param pred Selection predicate. */ template -__device__ void if_else(T* out, T const* true_value, T const* false_value, bool const* pred) +__device__ T if_else(T true_value, T false_value, bool pred) { - *out = *pred ? *true_value : *false_value; + return pred ? true_value : false_value; } /** * @brief Selects one of two values based on a predicate. * * @tparam T Selected value type. - * @param out Result destination. * @param true_value Optional value selected when @p pred is true. * @param false_value Optional value selected when @p pred is false or null. * @param pred Selection predicate. */ template -__device__ void if_else(cuda::std::optional* out, - cuda::std::optional const* true_value, - cuda::std::optional const* false_value, - cuda::std::optional const* pred) +__device__ optional if_else(optional true_value, optional false_value, optional pred) { - *out = pred->value_or(false) ? *true_value : *false_value; + return pred.value_or(false) ? true_value : false_value; } } // namespace ops diff --git a/cpp/include/cudf/operators/math.cuh b/cpp/include/cudf/operators/math.cuh index 996587203315..94cbdec2f73e 100644 --- a/cpp/include/cudf/operators/math.cuh +++ b/cpp/include/cudf/operators/math.cuh @@ -4,11 +4,14 @@ */ #pragma once +#include #include +#include +#include #include #include -#include +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -17,319 +20,159 @@ namespace ops { * @brief Computes cube root * * @tparam T Value type. - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void cbrt(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T cbrt(T a) { - *out = cuda::std::cbrt(*a); -} - -/** - * @brief Computes cube root. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cbrt(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - cbrt(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::cbrt(a); } /** * @brief Computes ceiling. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void ceil(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T ceil(T a) { - *out = cuda::std::ceil(*a); + return cuda::std::ceil(a); } /** * @brief Computes ceiling. * * @tparam R Decimal representation type. - * @param out Result destination. * @param a Input value. */ template -__device__ void ceil(numeric::decimal* out, numeric::decimal const* a) +__device__ numeric::decimal ceil(numeric::decimal a) { - if (a->scale() >= 0) { - *out = *a; + if (a.scale() >= 0) { + return a; } else { auto factor = - numeric::detail::ipow(-static_cast(a->scale())); - auto div = a->value() / factor; - auto rem = a->value() % factor; + numeric::detail::ipow(-static_cast(a.scale())); + auto div = a.value() / factor; + auto rem = a.value() % factor; if (rem == 0) { - *out = *a; + return a; } else { - auto val = a->value() > 0 ? (div + 1) : div; - *out = numeric::decimal{numeric::scaled_integer{val * factor, a->scale()}}; + auto val = a.value() > 0 ? (div + 1) : div; + return numeric::decimal{numeric::scaled_integer{val * factor, a.scale()}}; } } } -/** - * @brief Computes ceiling. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void ceil(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - ceil(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - /** * @brief Computes natural exponential. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void exp(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T exp(T a) { - *out = cuda::std::exp(*a); -} - -/** - * @brief Computes natural exponential. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void exp(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - exp(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::exp(a); } /** * @brief Computes floor of a value. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void floor(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T floor(T a) { - *out = cuda::std::floor(*a); + return cuda::std::floor(a); } /** * @brief Computes floor of a value. * * @tparam R Decimal representation type. - * @param out Result destination. * @param a Input value. */ template -__device__ void floor(numeric::decimal* out, numeric::decimal const* a) +__device__ numeric::decimal floor(numeric::decimal a) { - if (a->scale() >= 0) { - *out = *a; + if (a.scale() >= 0) { + return a; } else { auto factor = - numeric::detail::ipow(-static_cast(a->scale())); - auto div = a->value() / factor; - auto rem = a->value() % factor; + numeric::detail::ipow(-static_cast(a.scale())); + auto div = a.value() / factor; + auto rem = a.value() % factor; if (rem == 0) { - *out = *a; + return a; } else { - auto val = a->value() > 0 ? div : (div - 1); - *out = numeric::decimal{numeric::scaled_integer{val * factor, a->scale()}}; + auto val = a.value() > 0 ? div : (div - 1); + return numeric::decimal{numeric::scaled_integer{val * factor, a.scale()}}; } } } -/** - * @brief Computes floor of a value. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void floor(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - floor(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes natural logarithm. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ inline void log(T* out, T const* a) - requires(cuda::std::is_floating_point_v) -{ - *out = cuda::std::log(*a); -} - /** * @brief Computes natural logarithm. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. */ -template -__device__ void log(cuda::std::optional* out, cuda::std::optional const* a) +template +__device__ T log(T a) { - if (a->has_value()) { - T r; - log(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::log(a); } /** * @brief Computes exponentiation. * * @tparam T Value type. - * @param out Result destination. * @param a Base value. * @param b Exponent value. */ -template -__device__ inline void pow(T* out, T const* a, T const* b) - requires(cuda::std::is_floating_point_v) +template +__device__ T pow(T a, T b) { - *out = cuda::std::pow(*a, *b); + return cuda::std::pow(a, b); } /** * @brief Computes exponentiation. * * @tparam T Value type. - * @param out Result destination. * @param a Base value. * @param b Exponent value. */ -template -__device__ void pow(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +template +__device__ T pow(T a, T b) { - if (a->has_value() && b->has_value()) { - T r; - pow(&r, &a->value(), &b->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cudf::detail::integral_pow(a, b); } /** * @brief Rounds to integral value. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void rint(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T rint(T a) { - *out = cuda::std::rint(*a); -} - -/** - * @brief Rounds to integral value. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void rint(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - rint(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::rint(a); } /** * @brief Computes square root. * - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void sqrt(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T sqrt(T a) { - *out = cuda::std::sqrt(*a); -} - -/** - * @brief Computes square root. - * - * @tparam T Value type. - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void sqrt(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - sqrt(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::sqrt(a); } } // namespace ops diff --git a/cpp/include/cudf/operators/null_handling.cuh b/cpp/include/cudf/operators/null_handling.cuh index 06a876e75043..829693e2704b 100644 --- a/cpp/include/cudf/operators/null_handling.cuh +++ b/cpp/include/cudf/operators/null_handling.cuh @@ -5,6 +5,8 @@ #pragma once +#include +#include #include #include @@ -15,50 +17,46 @@ namespace ops { /** * @brief Tests whether an input value is null. * - * @tparam T Input type. - * @param out Result destination. + * @tparam T Value type. * @param a Input value. */ template -__device__ void is_null(bool* out, T const* a) +__device__ bool is_null(T a) + requires(!nullable) { - *out = false; + return false; } /** * @brief Tests whether an input value is null. * * @tparam T Value type. - * @param out Result destination. - * @param a Input value. */ template -__device__ void is_null(cuda::std::optional* out, cuda::std::optional const* a) +__device__ bool is_null(T a) + requires(nullable) { - *out = !a->has_value(); + return !a.has_value(); } /** * @brief Sets the output to null when the condition is true. * * @tparam T Value type. - * @param out Result destination. * @param a Input value. * @param condition boolean condition. */ template -__device__ void nullify_if(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* condition) +__device__ optional nullify_if(optional a, optional condition) { - if (condition->has_value() && a->has_value()) { - if (condition->value()) { - *out = cuda::std::nullopt; + if (condition.has_value() && a.has_value()) { + if (condition.value()) { + return {}; } else { - *out = a->value(); + return a.value(); } } else { - *out = cuda::std::nullopt; + return {}; } } @@ -66,58 +64,58 @@ __device__ void nullify_if(cuda::std::optional* out, * @brief Returns the first non-null of two values. * * @tparam T Value type. - * @param out Result destination. * @param a First value. * @param b Second value. */ template -__device__ void coalesce(T* out, T const* a, T const* b) +__device__ T coalesce(T a, T b) + requires(!nullable) { - *out = *a; + return a; } /** * @brief Returns the first non-null of two values. * * @tparam T Value type. - * @param out Result destination. * @param a First value. * @param b Second value. */ template -__device__ void coalesce(cuda::std::optional* out, - cuda::std::optional const* a, - cuda::std::optional const* b) +__device__ optional coalesce(optional a, optional b) { - if (a->has_value()) { - *out = a->value(); - } else if (b->has_value()) { - *out = b->value(); + if (a.has_value()) { + return a.value(); + } else if (b.has_value()) { + return b.value(); } else { - *out = cuda::std::nullopt; + return {}; } } /** * @brief Converts an optional predicate to a non-nullable predicate. * - * @param out Result destination. * @param a Input boolean predicate. */ -__device__ inline void predicate(bool* out, bool const* a) { *out = *a; } +template T> +__device__ inline bool predicate(T a) +{ + return a; +} /** * @brief Converts an optional predicate to a non-nullable predicate. * - * @param out Result destination. * @param a Optional input boolean predicate. */ -__device__ inline void predicate(cuda::std::optional* out, cuda::std::optional const* a) +template T> +__device__ inline bool predicate(optional a) { - if (a->has_value()) { - *out = a->value(); + if (a.has_value()) { + return a.value(); } else { - *out = false; + return false; } } diff --git a/cpp/include/cudf/operators/trigonometric.cuh b/cpp/include/cudf/operators/trigonometric.cuh index 3a2f55140ddd..19e10d475611 100644 --- a/cpp/include/cudf/operators/trigonometric.cuh +++ b/cpp/include/cudf/operators/trigonometric.cuh @@ -4,10 +4,12 @@ */ #pragma once +#include +#include #include #include -#include +#include namespace CUDF_EXPORT cudf { namespace ops { @@ -16,396 +18,144 @@ namespace ops { * @brief Computes inverse cosine. * * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void arccos(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T arccos(T a) { - *out = cuda::std::acos(*a); -} - -/** - * @brief Computes inverse cosine. - * - * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void arccos(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - arccos(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::acos(a); } /** * @brief Computes inverse hyperbolic cosine. * * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ inline void arccosh(T* out, T const* a) - requires(cuda::std::is_floating_point_v) -{ - *out = cuda::std::acosh(*a); -} - -/** - * @brief Computes inverse hyperbolic cosine. - * - * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void arccosh(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - arccosh(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes inverse sine. - * - * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void arcsin(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T arccosh(T a) { - *out = cuda::std::asin(*a); + return cuda::std::acosh(a); } /** * @brief Computes inverse sine. * * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ void arcsin(cuda::std::optional* out, cuda::std::optional const* a) +template +__device__ T arcsin(T a) { - if (a->has_value()) { - T r; - arcsin(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::asin(a); } /** * @brief Computes inverse hyperbolic sine. * * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void arcsinh(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T arcsinh(T a) { - *out = cuda::std::asinh(*a); -} - -/** - * @brief Computes inverse hyperbolic sine. - * - * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void arcsinh(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - arcsinh(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::asinh(a); } /** * @brief Computes inverse tangent. * * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ inline void arctan(T* out, T const* a) - requires(cuda::std::is_floating_point_v) -{ - *out = cuda::std::atan(*a); -} - -/** - * @brief Computes inverse tangent. - * - * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void arctan(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - arctan(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes inverse hyperbolic tangent. - * - * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void arctanh(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T arctan(T a) { - *out = cuda::std::atanh(*a); + return cuda::std::atan(a); } /** * @brief Computes inverse hyperbolic tangent. * * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ void arctanh(cuda::std::optional* out, cuda::std::optional const* a) +template +__device__ T arctanh(T a) { - if (a->has_value()) { - T r; - arctanh(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::atanh(a); } /** * @brief Computes cosine. * * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void cos(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T cos(T a) { - *out = cuda::std::cos(*a); -} - -/** - * @brief Computes cosine. - * - * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cos(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - cos(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::cos(a); } /** * @brief Computes hyperbolic cosine. * * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ inline void cosh(T* out, T const* a) - requires(cuda::std::is_floating_point_v) -{ - *out = cuda::std::cosh(*a); -} - -/** - * @brief Computes hyperbolic cosine. - * - * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void cosh(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - cosh(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes sine. - * - * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void sin(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T cosh(T a) { - *out = cuda::std::sin(*a); + return cuda::std::cosh(a); } /** * @brief Computes sine. * * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ void sin(cuda::std::optional* out, cuda::std::optional const* a) +template +__device__ T sin(T a) { - if (a->has_value()) { - T r; - sin(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::sin(a); } /** * @brief Computes hyperbolic sine. * * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void sinh(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T sinh(T a) { - *out = cuda::std::sinh(*a); -} - -/** - * @brief Computes hyperbolic sine. - * - * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void sinh(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - sinh(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::sinh(a); } /** * @brief Computes tangent. * * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ inline void tan(T* out, T const* a) - requires(cuda::std::is_floating_point_v) -{ - *out = cuda::std::tan(*a); -} - -/** - * @brief Computes tangent. - * - * @tparam T Value type - * @param out Result destination. - * @param a Input value. - */ -template -__device__ void tan(cuda::std::optional* out, cuda::std::optional const* a) -{ - if (a->has_value()) { - T r; - tan(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } -} - -/** - * @brief Computes hyperbolic tangent. - * - * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ inline void tanh(T* out, T const* a) - requires(cuda::std::is_floating_point_v) +template +__device__ T tan(T a) { - *out = cuda::std::tanh(*a); + return cuda::std::tan(a); } /** * @brief Computes hyperbolic tangent. * * @tparam T Value type - * @param out Result destination. * @param a Input value. */ -template -__device__ void tanh(cuda::std::optional* out, cuda::std::optional const* a) +template +__device__ T tanh(T a) { - if (a->has_value()) { - T r; - tanh(&r, &a->value()); - *out = r; - } else { - *out = cuda::std::nullopt; - } + return cuda::std::tanh(a); } } // namespace ops diff --git a/cpp/include/cudf/operators/types.cuh b/cpp/include/cudf/operators/types.cuh new file mode 100644 index 000000000000..c349849027a6 --- /dev/null +++ b/cpp/include/cudf/operators/types.cuh @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +#include + +namespace CUDF_EXPORT cudf { +namespace ops { + +template +using optional = cuda::std::optional; + +} // namespace ops +} // namespace CUDF_EXPORT cudf diff --git a/cpp/src/ast/operators.cpp b/cpp/src/ast/operators.cpp index eaacabde0b74..35ea746dec9c 100644 --- a/cpp/src/ast/operators.cpp +++ b/cpp/src/ast/operators.cpp @@ -21,8 +21,7 @@ struct arity_functor { template void operator()(cudf::size_type& result) { - // Arity is not dependent on null handling, so just use the false implementation here. - result = operator_functor::arity; + result = operator_functor::arity; } }; @@ -156,7 +155,7 @@ struct type_dispatch_binary_op { type_dispatcher( lhs_type, // Always dispatch to the non-null operator for the purpose of type determination. - detail::single_dispatch_binary_operator_types>{}, + detail::single_dispatch_binary_operator_types>{}, std::forward(f), std::forward(args)...); } @@ -225,7 +224,7 @@ struct type_dispatch_unary_op { type_dispatcher( input_type, // Always dispatch to the non-null operator for the purpose of type determination. - detail::dispatch_unary_operator_types>{}, + detail::dispatch_unary_operator_types>{}, std::forward(f), std::forward(args)...); } diff --git a/cpp/src/jit/row_ir.cpp b/cpp/src/jit/row_ir.cpp index 0d3b58dc89e1..908f359920dc 100644 --- a/cpp/src/jit/row_ir.cpp +++ b/cpp/src/jit/row_ir.cpp @@ -375,12 +375,11 @@ void node::emit_code(instance_context& instance, target_info const& info, code_s args_str)); } else { sink.emit(std::format( - R"***({} {} = cudf::ast::detail::operator_functor{{}}({}); + R"***({} {} = cudf::ast::detail::operator_functor{{}}({}); )***", type, id_, ast::detail::ast_operator_string(as_ast_op(op_)), - instance.has_nulls(), args_str)); } } break; From a014c89f6a24280182a5c06293121e6687398ad4 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Sat, 30 May 2026 18:52:06 +0000 Subject: [PATCH 27/34] Refactor operator functor calls to remove unnecessary 'false' parameter in unary and binary operations --- cpp/tests/jit/row_ir.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cpp/tests/jit/row_ir.cpp b/cpp/tests/jit/row_ir.cpp index b1e5d1cd0a3b..812f6409d512 100644 --- a/cpp/tests/jit/row_ir.cpp +++ b/cpp/tests/jit/row_ir.cpp @@ -136,7 +136,7 @@ TEST_F(RowIRCudaCodeGenTest, UnaryOperation) auto expected_code = R"***(int32_t tmp_0 = in_0; -int32_t tmp_1 = cudf::ast::detail::operator_functor{}(tmp_0); +int32_t tmp_1 = cudf::ast::detail::operator_functor{}(tmp_0); )***"; EXPECT_EQ(sink.get_code(), expected_code); @@ -155,7 +155,7 @@ int32_t tmp_1 = cudf::ast::detail::operator_functor{}(tmp_0); +numeric::decimal32 tmp_1 = cudf::ast::detail::operator_functor{}(tmp_0); )***"; EXPECT_EQ(sink.get_code(), expected_null_code); @@ -182,7 +182,7 @@ TEST_F(RowIRCudaCodeGenTest, BinaryOperation) auto expected_code = R"***(int32_t tmp_0 = in_0; int32_t tmp_1 = in_0; -int32_t tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); +int32_t tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); )***"; EXPECT_EQ(sink.get_code(), expected_code); @@ -204,7 +204,7 @@ int32_t tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); +numeric::decimal32 tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); )***"; EXPECT_EQ(sink.get_code(), expected_null_code); @@ -251,12 +251,12 @@ TEST_F(RowIRCudaCodeGenTest, VectorLengthOperation) auto expected_code = R"***(double tmp_0 = in_0; double tmp_1 = in_0; -double tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); +double tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); double tmp_3 = in_1; double tmp_4 = in_1; -double tmp_5 = cudf::ast::detail::operator_functor{}(tmp_3, tmp_4); -double tmp_6 = cudf::ast::detail::operator_functor{}(tmp_2, tmp_5); -double tmp_7 = cudf::ast::detail::operator_functor{}(tmp_6); +double tmp_5 = cudf::ast::detail::operator_functor{}(tmp_3, tmp_4); +double tmp_6 = cudf::ast::detail::operator_functor{}(tmp_2, tmp_5); +double tmp_7 = cudf::ast::detail::operator_functor{}(tmp_6); double tmp_8 = tmp_7; *out_0 = tmp_8; )***"; @@ -317,7 +317,7 @@ TEST_F(RowIRCudaCodeGenTest, AstConversionBasic) { int32_t tmp_0 = in_0; int32_t tmp_1 = in_1; -int32_t tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); +int32_t tmp_2 = cudf::ast::detail::operator_functor{}(tmp_0, tmp_1); int32_t tmp_3 = tmp_2; *out_0 = tmp_3; return; From 172524f91a1be4a4954480961356c67c5baf17fc Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Sat, 30 May 2026 20:04:24 +0000 Subject: [PATCH 28/34] Update predicate references to use cudf::ops in row_ir code generation --- cpp/src/jit/row_ir.cpp | 2 +- cpp/tests/jit/row_ir.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/src/jit/row_ir.cpp b/cpp/src/jit/row_ir.cpp index 908f359920dc..09a40cf4b746 100644 --- a/cpp/src/jit/row_ir.cpp +++ b/cpp/src/jit/row_ir.cpp @@ -369,7 +369,7 @@ void node::emit_code(instance_context& instance, target_info const& info, code_s if (op_ == opcode::PREDICATE) { sink.emit(std::format( - R"***(bool {} = cudf::ast::detail::predicate({}); + R"***(bool {} = cudf::ops::predicate({}); )***", id_, args_str)); diff --git a/cpp/tests/jit/row_ir.cpp b/cpp/tests/jit/row_ir.cpp index 812f6409d512..551f89f1a5aa 100644 --- a/cpp/tests/jit/row_ir.cpp +++ b/cpp/tests/jit/row_ir.cpp @@ -352,7 +352,7 @@ TEST_F(RowIRCudaCodeGenTest, FilterPredicate) filter_predicate.emit_code(ctx, target_info, sink); auto expected_code = R"***(bool tmp_0 = in_0; -bool tmp_1 = cudf::ast::detail::predicate(tmp_0); +bool tmp_1 = cudf::ops::predicate(tmp_0); )***"; EXPECT_EQ(sink.get_code(), expected_code); @@ -370,7 +370,7 @@ bool tmp_1 = cudf::ast::detail::predicate(tmp_0); filter_predicate.emit_code(ctx, target_info, sink); auto expected_code = R"***(cuda::std::optional tmp_0 = in_0; -bool tmp_1 = cudf::ast::detail::predicate(tmp_0); +bool tmp_1 = cudf::ops::predicate(tmp_0); )***"; EXPECT_EQ(sink.get_code(), expected_code); From e6da4320ac00cb474e50f3b4533690242214483d Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Sat, 30 May 2026 20:47:56 +0000 Subject: [PATCH 29/34] Add ordered concept for pre-CUDA 12.3 compatibility and update comparison requirements --- cpp/include/cudf/operators/comparison.cuh | 8 ++++---- cpp/include/cudf/operators/concepts.cuh | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/cpp/include/cudf/operators/comparison.cuh b/cpp/include/cudf/operators/comparison.cuh index bed06e38add0..13e8aaf380a0 100644 --- a/cpp/include/cudf/operators/comparison.cuh +++ b/cpp/include/cudf/operators/comparison.cuh @@ -50,7 +50,7 @@ __device__ bool not_equal(T a, T b) */ template __device__ bool greater(T a, T b) - requires(!nullable && cuda::std::totally_ordered) + requires(!nullable && ordered) { return (a > b); } @@ -64,7 +64,7 @@ __device__ bool greater(T a, T b) */ template __device__ bool greater_equal(T a, T b) - requires(!nullable && cuda::std::totally_ordered) + requires(!nullable && ordered) { return (a >= b); } @@ -78,7 +78,7 @@ __device__ bool greater_equal(T a, T b) */ template __device__ bool less(T a, T b) - requires(!nullable && cuda::std::totally_ordered) + requires(!nullable && ordered) { return (a < b); } @@ -92,7 +92,7 @@ __device__ bool less(T a, T b) */ template __device__ bool less_equal(T a, T b) - requires(!nullable && cuda::std::totally_ordered) + requires(!nullable && ordered) { return (a <= b); } diff --git a/cpp/include/cudf/operators/concepts.cuh b/cpp/include/cudf/operators/concepts.cuh index 093463c43991..d16e01e977d0 100644 --- a/cpp/include/cudf/operators/concepts.cuh +++ b/cpp/include/cudf/operators/concepts.cuh @@ -50,5 +50,14 @@ constexpr bool is_nullable> = true; template concept nullable = is_nullable; +// Workaround for pre-CUDA 12.3 CCCL not correctly detecting ordering. +template +concept ordered = requires(T a, T b) { + { a < b } -> cuda::std::convertible_to; + { a > b } -> cuda::std::convertible_to; + { a <= b } -> cuda::std::convertible_to; + { a >= b } -> cuda::std::convertible_to; +}; + } // namespace ops } // namespace CUDF_EXPORT cudf From 106cbc946f1c7aadb8ba4ba0d32d5acd97e38503 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Sat, 30 May 2026 22:48:30 +0000 Subject: [PATCH 30/34] Refactor operator functions to use template type parameters for improved type flexibility and remove redundant comments --- .../cudf/ast/detail/operator_functor.cuh | 18 +-- .../cudf/operators/ansi_arithmetic.cuh | 93 -------------- cpp/include/cudf/operators/arithmetic.cuh | 121 +++++++----------- cpp/include/cudf/operators/bitwise.cuh | 47 ++++--- cpp/include/cudf/operators/casts.cuh | 12 -- cpp/include/cudf/operators/comparison.cuh | 90 ++++++------- cpp/include/cudf/operators/concepts.cuh | 19 --- cpp/include/cudf/operators/logic.cuh | 58 +++------ cpp/include/cudf/operators/math.cuh | 30 +---- cpp/include/cudf/operators/null_handling.cuh | 17 --- 10 files changed, 149 insertions(+), 356 deletions(-) diff --git a/cpp/include/cudf/ast/detail/operator_functor.cuh b/cpp/include/cudf/ast/detail/operator_functor.cuh index e559f841f048..fe487f4b72dd 100644 --- a/cpp/include/cudf/ast/detail/operator_functor.cuh +++ b/cpp/include/cudf/ast/detail/operator_functor.cuh @@ -19,15 +19,15 @@ namespace ast::detail { template struct operator_invoker; -#define CUDF_AST_OPERATOR_MAP(OP, func_name, num_args) \ - template <> \ - struct operator_invoker { \ - static constexpr auto arity = num_args; \ - __device__ static inline auto eval(auto... a) \ - requires(requires { cudf::ops::func_name(a...); }) \ - { \ - return cudf::ops::func_name(a...); \ - } \ +#define CUDF_AST_OPERATOR_MAP(OP, func_name, num_args) \ + template <> \ + struct operator_invoker { \ + static constexpr auto arity = num_args; \ + template \ + __device__ static inline auto eval(Args... a) -> decltype(cudf::ops::func_name(a...)) \ + { \ + return cudf::ops::func_name(a...); \ + } \ }; CUDF_AST_OPERATOR_MAP(ADD, add, 2) diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/operators/ansi_arithmetic.cuh index 0986f116b247..c10b5f2e181c 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/operators/ansi_arithmetic.cuh @@ -35,12 +35,6 @@ __device__ result ansi_add(T a, T b) return r; } -/** - * @brief Adds operands with overflow detection. - * - * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else the result. - */ template __device__ result ansi_add(T a, T b) { @@ -66,14 +60,6 @@ __device__ result> ansi_add(numeric::decimal a, numeric:: a.rescaled(scale).value() + b.rescaled(scale).value(), numeric::scale_type{scale}}}; } -/** - * @brief Subtracts operands with overflow detection. - * - * @tparam T Value type. - * @param a Minuend. - * @param b Subtrahend. - * @return `errc::OVERFLOW` on overflow, else the result. - */ template __device__ result ansi_sub(T a, T b) { @@ -94,12 +80,6 @@ __device__ result ansi_sub(T a, T b) return a - b; } -/** - * @brief Subtracts operands with overflow detection. - * - * @tparam R Decimal representation type. - * @return `errc::OVERFLOW` on overflow, else the result. - */ template __device__ result> ansi_sub(numeric::decimal a, numeric::decimal b) { @@ -129,24 +109,12 @@ __device__ result ansi_mul(T a, T b) return r; } -/** - * @brief Multiplies operands with overflow detection. - * - * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else the result. - */ template __device__ result ansi_mul(T a, T b) { return a * b; } -/** - * @brief Multiplies operands with overflow detection. - * - * @tparam R Decimal representation type. - * @return `errc::OVERFLOW` on overflow, else the result. - */ template __device__ result> ansi_mul(numeric::decimal a, numeric::decimal b) { @@ -174,25 +142,12 @@ __device__ result ansi_div(T a, T b) return r; } -/** - * @brief Divides operands with ANSI checks. - * - * @tparam T Floating-point type. - * @return errc::SUCCESS. - */ template __device__ result ansi_div(T a, T b) { return a / b; } -/** - * @brief Divides operands with ANSI checks. - * - * @tparam R Decimal representation type. - * @return `errc::DIVISION_BY_ZERO` on zero divisor, `errc::OVERFLOW` on overflow, else - * `errc::SUCCESS`. - */ template __device__ result> ansi_div(numeric::decimal a, numeric::decimal b) { @@ -223,12 +178,6 @@ __device__ result ansi_mod(T a, T b) return a % b; } -/** - * @brief Computes modulus with ANSI checks. - * - * @tparam T Value type. - * @return `errc::DIVISION_BY_ZERO` on zero divisor, else the result. - */ template __device__ result ansi_mod(T a, T b) { @@ -236,11 +185,6 @@ __device__ result ansi_mod(T a, T b) return a % b; } -/** - * @brief Computes modulus with ANSI checks. - * - * @return `errc::DIVISION_BY_ZERO` on zero divisor, else the result. - */ template __device__ inline result ansi_mod(T a, T b) { @@ -248,12 +192,6 @@ __device__ inline result ansi_mod(T a, T b) return a - b * cuda::std::floor(a / b); } -/** - * @brief Computes modulus with ANSI checks. - * - * @tparam R Decimal representation type. - * @return `errc::DIVISION_BY_ZERO` on zero divisor, else the result. - */ template __device__ result> ansi_mod(numeric::decimal a, numeric::decimal b) { @@ -276,36 +214,18 @@ __device__ result ansi_abs(T a) return (a < 0) ? -a : a; } -/** - * @brief Computes absolute value with ANSI overflow checks. - * - * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else the result. - */ template __device__ result ansi_abs(T a) { return a; } -/** - * @brief Computes absolute value with ANSI overflow checks. - * - * @tparam T Value type. - * @return `errc::OVERFLOW` on overflow, else the result. - */ template __device__ result ansi_abs(T a) { return cuda::std::fabs(a); } -/** - * @brief Computes absolute value with ANSI overflow checks. - * - * @tparam R Decimal representation type. - * @return `errc::OVERFLOW` on overflow, else errc::SUCCESS. - */ template __device__ result> ansi_abs(numeric::decimal a) { @@ -328,25 +248,12 @@ __device__ result ansi_neg(T a) return -a; } -/** - * @brief Computes unary negation with ANSI overflow checks. - * - * @tparam T Value type. - * @param a Input value. - * @return `errc::OVERFLOW` on overflow, else the result. - */ template __device__ result ansi_neg(T a) { return -a; } -/** - * @brief Computes unary negation with ANSI overflow checks. - * - * @tparam R Decimal representation type. - * @return `errc::OVERFLOW` on overflow, else the result. - */ template __device__ result> ansi_neg(numeric::decimal a) { diff --git a/cpp/include/cudf/operators/arithmetic.cuh b/cpp/include/cudf/operators/arithmetic.cuh index a4c7de5a7b2f..f73f46338378 100644 --- a/cpp/include/cudf/operators/arithmetic.cuh +++ b/cpp/include/cudf/operators/arithmetic.cuh @@ -30,23 +30,12 @@ __device__ T abs(T a) return cuda::std::abs(a); } -/** - * @brief Computes absolute value. - * - * @tparam T Value type. - * @param a Input value. - */ template __device__ T abs(T a) { return a; } -/** - * @brief Computes absolute value. - * - * @tparam R Decimal representation type. - */ template __device__ numeric::decimal abs(numeric::decimal a) { @@ -57,51 +46,47 @@ __device__ numeric::decimal abs(numeric::decimal a) /** * @brief Computes sum of two values. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ T add(T a, T b) +template +__device__ auto add(A a, B b) -> decltype(a + b) { - return (a + b); + return a + b; } /** * @brief Computes quotient of two values. * - * @tparam T Value type. + * @tparam A Dividend type. + * @tparam B Divisor type. * @param a Dividend. * @param b Divisor. */ -template -__device__ T div(T a, T b) +template +__device__ auto div(A a, B b) -> decltype(a / b) { - return (a / b); + return a / b; } /** * @brief Computes floor division of two values. * - * @tparam T Value type. + * @tparam A Dividend type. + * @tparam B Divisor type. * @param a Dividend. * @param b Divisor. */ -template -__device__ T floor_div(T a, T b) +template +__device__ auto floor_div(A a, B b) -> decltype(cudf::detail::integral_floor_div(a, b)) { return cudf::detail::integral_floor_div(a, b); } -/** - * @brief Computes floor division of two values. - * - * @tparam T Value type. - * @param a Dividend. - * @param b Divisor. - */ -template -__device__ T floor_div(T a, T b) +template +__device__ auto floor_div(A a, B b) -> decltype(cuda::std::floor(a / b)) { return cuda::std::floor(a / b); } @@ -109,27 +94,19 @@ __device__ T floor_div(T a, T b) /** * @brief Computes remainder of two values. * - * @tparam T Value type. + * @tparam A Dividend type. + * @tparam B Divisor type. * @param a Dividend. * @param b Divisor. */ -template -__device__ T mod(T a, T b) - requires(integer || fixed_point) +template +__device__ auto mod(A a, B b) -> decltype(a % b) { - return (a % b); + return a % b; } -/** - * @brief Computes remainder of two values. - * - * @tparam T Value type. - * @param a Dividend. - * @param b Divisor. - */ -template -__device__ T mod(T a, T b) - requires(floating_point) +template +__device__ auto mod(A a, B b) -> decltype(cuda::std::fmod(a, b)) { return cuda::std::fmod(a, b); } @@ -137,26 +114,19 @@ __device__ T mod(T a, T b) /** * @brief Computes Python-style modulus. * - * @tparam T Value type. + * @tparam A Dividend type. + * @tparam B Divisor type. * @param a Dividend. * @param b Divisor. */ -template -__device__ T pymod(T a, T b) - requires(integer || fixed_point) +template +__device__ auto pymod(A a, B b) -> decltype((a % b + b) % b) { return (a % b + b) % b; } -/** - * @brief Computes Python-style modulus. - * - * @tparam T Value type. - * @param a Dividend. - * @param b Divisor. - */ -template -__device__ T pymod(T a, T b) +template +__device__ auto pymod(A a, B b) -> decltype(cuda::std::fmod(cuda::std::fmod(a, b) + b, b)) { return cuda::std::fmod(cuda::std::fmod(a, b) + b, b); } @@ -164,14 +134,15 @@ __device__ T pymod(T a, T b) /** * @brief Computes product of two values. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ T mul(T a, T b) +template +__device__ auto mul(A a, B b) -> decltype(a * b) { - return (a * b); + return a * b; } /** @@ -181,18 +152,11 @@ __device__ T mul(T a, T b) * @param a Input value. */ template -__device__ T neg(T a) - requires(signed_integer || floating_point) +__device__ auto neg(T a) -> decltype(-a) { return -a; } -/** - * @brief Computes unary negation. - * - * @tparam R Decimal representation type. - * @param a Input value. - */ template __device__ numeric::decimal neg(numeric::decimal a) { @@ -203,12 +167,13 @@ __device__ numeric::decimal neg(numeric::decimal a) /** * @brief Computes subtraction of two values. * - * @tparam T Value type. + * @tparam A Minuend type. + * @tparam B Subtrahend type. * @param a Minuend. * @param b Subtrahend. */ -template -__device__ T sub(T a, T b) +template +__device__ auto sub(A a, B b) -> decltype(a - b) { return a - b; } @@ -216,13 +181,13 @@ __device__ T sub(T a, T b) /** * @brief Computes true division and returns a double. * - * @tparam T Value type. + * @tparam A Dividend type. + * @tparam B Divisor type. * @param a Dividend. * @param b Divisor. */ -template -__device__ double true_div(T a, T b) - requires(floating_point || integer) +template +__device__ auto true_div(A a, B b) -> decltype(static_cast(a) / static_cast(b)) { return static_cast(a) / static_cast(b); } diff --git a/cpp/include/cudf/operators/bitwise.cuh b/cpp/include/cudf/operators/bitwise.cuh index ef9d5abfe7f7..0b0b560105fb 100644 --- a/cpp/include/cudf/operators/bitwise.cuh +++ b/cpp/include/cudf/operators/bitwise.cuh @@ -14,14 +14,15 @@ namespace ops { /** * @brief Computes bitwise AND of two values. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ T bit_and(T a, T b) +template +__device__ auto bit_and(A a, B b) -> decltype(a & b) { - return (a & b); + return a & b; } /** @@ -31,7 +32,7 @@ __device__ T bit_and(T a, T b) * @param a Input value. */ template -__device__ T bit_invert(T a) +__device__ auto bit_invert(T a) -> decltype(~a) { return ~a; } @@ -39,53 +40,57 @@ __device__ T bit_invert(T a) /** * @brief Computes bitwise OR of two values. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ T bit_or(T a, T b) +template +__device__ auto bit_or(A a, B b) -> decltype(a | b) { - return (a | b); + return a | b; } /** * @brief Computes bitwise XOR of two values. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ T bit_xor(T a, T b) +template +__device__ auto bit_xor(A a, B b) -> decltype(a ^ b) { - return (a ^ b); + return a ^ b; } /** * @brief Shifts a value left by a bit count. * - * @tparam T Value type. + * @tparam A Value type. + * @tparam B Shift count type. * @param a Input value. * @param b Shift count. */ -template -__device__ T bit_shift_left(T a, T b) +template +__device__ auto bit_shift_left(A a, B b) -> decltype(a << b) { - return (a << b); + return a << b; } /** * @brief Shifts a value right by a bit count. * - * @tparam T Value type. + * @tparam A Value type. + * @tparam B Shift count type. * @param a Input value. * @param b Shift count. */ -template -__device__ T bit_shift_right(T a, T b) +template +__device__ auto bit_shift_right(A a, B b) -> decltype(a >> b) { - return (a >> b); + return a >> b; } } // namespace ops diff --git a/cpp/include/cudf/operators/casts.cuh b/cpp/include/cudf/operators/casts.cuh index 30d87e127007..0f8c61cc50e9 100644 --- a/cpp/include/cudf/operators/casts.cuh +++ b/cpp/include/cudf/operators/casts.cuh @@ -145,12 +145,6 @@ __device__ float cast_to_f32(T a) return static_cast(a); } -/** - * @brief Casts fixed-point decimal values to float. - * - * @tparam R Source decimal representation type. - * @param a Source decimal value. - */ template __device__ float cast_to_f32(numeric::decimal a) { @@ -170,12 +164,6 @@ __device__ double cast_to_f64(T a) return static_cast(a); } -/** - * @brief Casts input values to double. - * - * @tparam R Source decimal representation type. - * @param a Input value. - */ template __device__ double cast_to_f64(numeric::decimal a) { diff --git a/cpp/include/cudf/operators/comparison.cuh b/cpp/include/cudf/operators/comparison.cuh index 13e8aaf380a0..785e04655eac 100644 --- a/cpp/include/cudf/operators/comparison.cuh +++ b/cpp/include/cudf/operators/comparison.cuh @@ -16,111 +16,111 @@ namespace ops { /** * @brief Tests `a == b`. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool equal(T a, T b) - requires(!nullable && cuda::std::equality_comparable) +template +__device__ bool equal(A a, B b) + requires(!nullable && !nullable && requires { a == b; }) { - return (a == b); + return a == b; } /** * @brief Tests `a != b`. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool not_equal(T a, T b) - requires(!nullable && cuda::std::equality_comparable) +template +__device__ bool not_equal(A a, B b) + requires(!nullable && !nullable && requires { a != b; }) { - return (a != b); + return a != b; } /** * @brief Tests `a > b`. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool greater(T a, T b) - requires(!nullable && ordered) +template +__device__ bool greater(A a, B b) + requires(!nullable && !nullable && requires { a > b; }) { - return (a > b); + return a > b; } /** * @brief Tests `a >= b`. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool greater_equal(T a, T b) - requires(!nullable && ordered) +template +__device__ bool greater_equal(A a, B b) + requires(!nullable && !nullable && requires { a >= b; }) { - return (a >= b); + return a >= b; } /** * @brief Tests `a < b`. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool less(T a, T b) - requires(!nullable && ordered) +template +__device__ bool less(A a, B b) + requires(!nullable && !nullable && requires { a < b; }) { - return (a < b); + return a < b; } /** * @brief Tests `a <= b`. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool less_equal(T a, T b) - requires(!nullable && ordered) +template +__device__ bool less_equal(A a, B b) + requires(!nullable && !nullable && requires { a <= b; }) { - return (a <= b); + return a <= b; } /** * @brief Tests equality between two values for null-aware equality semantics. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool null_equal(T a, T b) - requires(!nullable && cuda::std::equality_comparable) +template +__device__ bool null_equal(A a, B b) + requires(!nullable && !nullable && requires { a == b; }) { - return (a == b); + return a == b; } -/** - * @brief Tests equality between two values for null-aware equality semantics. - * - * @tparam T Value type. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ bool null_equal(optional a, optional b) - requires(!nullable && cuda::std::equality_comparable) +template +__device__ bool null_equal(optional a, optional b) + requires(!nullable && !nullable && requires { a == b; }) { if (a.has_value() && b.has_value()) { return null_equal(a.value(), b.value()); diff --git a/cpp/include/cudf/operators/concepts.cuh b/cpp/include/cudf/operators/concepts.cuh index d16e01e977d0..47b6052d6333 100644 --- a/cpp/include/cudf/operators/concepts.cuh +++ b/cpp/include/cudf/operators/concepts.cuh @@ -15,16 +15,6 @@ namespace CUDF_EXPORT cudf { namespace ops { -template -concept arithmetic = - (cuda::std::is_arithmetic_v && !cuda::std::is_same_v, bool>) || - cudf::is_fixed_point(); - -template -concept integral_or_floating_point_not_bool = - (cuda::std::is_integral_v || cuda::std::is_floating_point_v) && - !cuda::std::is_same_v, bool>; - template concept integer = cuda::std::is_integral_v && !cuda::std::is_same_v, bool>; @@ -50,14 +40,5 @@ constexpr bool is_nullable> = true; template concept nullable = is_nullable; -// Workaround for pre-CUDA 12.3 CCCL not correctly detecting ordering. -template -concept ordered = requires(T a, T b) { - { a < b } -> cuda::std::convertible_to; - { a > b } -> cuda::std::convertible_to; - { a <= b } -> cuda::std::convertible_to; - { a >= b } -> cuda::std::convertible_to; -}; - } // namespace ops } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/logic.cuh b/cpp/include/cudf/operators/logic.cuh index 807d02a4406a..52467431aefa 100644 --- a/cpp/include/cudf/operators/logic.cuh +++ b/cpp/include/cudf/operators/logic.cuh @@ -16,25 +16,19 @@ namespace ops { /** * @brief Computes logical AND with null-aware semantics. * - * @tparam T Operand type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool null_logical_and(T a, T b) +template +__device__ bool null_logical_and(A a, B b) { return a && b; } -/** - * @brief Computes logical AND with null-aware semantics. - * - * @tparam T Value type. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ optional null_logical_and(optional a, optional b) +template +__device__ optional null_logical_and(optional a, optional b) { if (a.has_value() && b.has_value()) { return null_logical_and(a.value(), b.value()); @@ -52,25 +46,19 @@ __device__ optional null_logical_and(optional a, optional b) /** * @brief Computes logical OR with null-aware semantics. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool null_logical_or(T a, T b) +template +__device__ bool null_logical_or(A a, B b) { return a || b; } -/** - * @brief Computes logical OR with null-aware semantics. - * - * @tparam T Value type. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ optional null_logical_or(optional a, optional b) +template +__device__ optional null_logical_or(optional a, optional b) { if (a.has_value() && b.has_value()) { return null_logical_or(a.value(), b.value()); @@ -88,12 +76,13 @@ __device__ optional null_logical_or(optional a, optional b) /** * @brief Computes logical AND. * - * @tparam T Operand type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool logical_and(T a, T b) +template +__device__ bool logical_and(A a, B b) { return a && b; } @@ -101,12 +90,13 @@ __device__ bool logical_and(T a, T b) /** * @brief Computes logical OR. * - * @tparam T Value type. + * @tparam A Left operand type. + * @tparam B Right operand type. * @param a Left operand. * @param b Right operand. */ -template -__device__ bool logical_or(T a, T b) +template +__device__ bool logical_or(A a, B b) { return a || b; } @@ -137,14 +127,6 @@ __device__ T if_else(T true_value, T false_value, bool pred) return pred ? true_value : false_value; } -/** - * @brief Selects one of two values based on a predicate. - * - * @tparam T Selected value type. - * @param true_value Optional value selected when @p pred is true. - * @param false_value Optional value selected when @p pred is false or null. - * @param pred Selection predicate. - */ template __device__ optional if_else(optional true_value, optional false_value, optional pred) { diff --git a/cpp/include/cudf/operators/math.cuh b/cpp/include/cudf/operators/math.cuh index 94cbdec2f73e..d1cb1320bd3a 100644 --- a/cpp/include/cudf/operators/math.cuh +++ b/cpp/include/cudf/operators/math.cuh @@ -40,12 +40,6 @@ __device__ T ceil(T a) return cuda::std::ceil(a); } -/** - * @brief Computes ceiling. - * - * @tparam R Decimal representation type. - * @param a Input value. - */ template __device__ numeric::decimal ceil(numeric::decimal a) { @@ -89,12 +83,6 @@ __device__ T floor(T a) return cuda::std::floor(a); } -/** - * @brief Computes floor of a value. - * - * @tparam R Decimal representation type. - * @param a Input value. - */ template __device__ numeric::decimal floor(numeric::decimal a) { @@ -129,25 +117,19 @@ __device__ T log(T a) /** * @brief Computes exponentiation. * - * @tparam T Value type. + * @tparam A Base value type. + * @tparam B Exponent value type. * @param a Base value. * @param b Exponent value. */ -template -__device__ T pow(T a, T b) +template +__device__ auto pow(A a, B b) -> decltype(cuda::std::pow(a, b)) { return cuda::std::pow(a, b); } -/** - * @brief Computes exponentiation. - * - * @tparam T Value type. - * @param a Base value. - * @param b Exponent value. - */ -template -__device__ T pow(T a, T b) +template +__device__ auto pow(A a, B b) -> decltype(cudf::detail::integral_pow(a, b)) { return cudf::detail::integral_pow(a, b); } diff --git a/cpp/include/cudf/operators/null_handling.cuh b/cpp/include/cudf/operators/null_handling.cuh index 829693e2704b..0e3b7278f182 100644 --- a/cpp/include/cudf/operators/null_handling.cuh +++ b/cpp/include/cudf/operators/null_handling.cuh @@ -27,11 +27,6 @@ __device__ bool is_null(T a) return false; } -/** - * @brief Tests whether an input value is null. - * - * @tparam T Value type. - */ template __device__ bool is_null(T a) requires(nullable) @@ -74,13 +69,6 @@ __device__ T coalesce(T a, T b) return a; } -/** - * @brief Returns the first non-null of two values. - * - * @tparam T Value type. - * @param a First value. - * @param b Second value. - */ template __device__ optional coalesce(optional a, optional b) { @@ -104,11 +92,6 @@ __device__ inline bool predicate(T a) return a; } -/** - * @brief Converts an optional predicate to a non-nullable predicate. - * - * @param a Optional input boolean predicate. - */ template T> __device__ inline bool predicate(optional a) { From b60e31a8a5e7848e859b7f95b3faf690e1d4f769 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Sun, 31 May 2026 08:37:36 +0000 Subject: [PATCH 31/34] update --- cpp/include/cudf/operators/error.hpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/cpp/include/cudf/operators/error.hpp b/cpp/include/cudf/operators/error.hpp index d50b0c763943..19d27d5dee0d 100644 --- a/cpp/include/cudf/operators/error.hpp +++ b/cpp/include/cudf/operators/error.hpp @@ -35,21 +35,21 @@ struct result { * * @param value The value of the result. */ - __device__ constexpr result(T value) : error_(errc::SUCCESS), value_(value) {} + __device__ __host__ constexpr result(T value) : error_(errc::SUCCESS), value_(value) {} /** * @brief Constructs a result with an error. * * @param error The error code of the result. */ - __device__ constexpr result(errc error) : error_(error), value_() {} + __device__ __host__ constexpr result(errc error) : error_(error), value_() {} /** * @brief Checks if the result has an error. * * @return true if the result has an error, false otherwise. */ - [[nodiscard]] __device__ __host__ constexpr bool has_error() const noexcept + [[nodiscard]] __device__ __host__ constexpr bool has_error() const { return error_ != errc::SUCCESS; } @@ -59,10 +59,7 @@ struct result { * * @return true if the result has a value, false otherwise. */ - [[nodiscard]] __device__ __host__ constexpr bool has_value() const noexcept - { - return !has_error(); - } + [[nodiscard]] __device__ __host__ constexpr bool has_value() const { return !has_error(); } /** * @brief Returns true if the result has a value, false otherwise. This operator allows the result @@ -70,10 +67,7 @@ struct result { * * @return true if the result has a value, false otherwise. */ - [[nodiscard]] __device__ __host__ constexpr explicit operator bool() const noexcept - { - return has_value(); - } + [[nodiscard]] __device__ __host__ constexpr explicit operator bool() const { return has_value(); } /** * @brief Returns the value of the result. Behaviour is undefined if the result has an error (i.e. From 3a85d86635cabd22e4c1f1f7ed2445673ab758fe Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Mon, 1 Jun 2026 15:44:36 +0000 Subject: [PATCH 32/34] Refactor result struct to use CUDF_HOST_DEVICE for device and host compatibility, and add value_or_null method for improved error handling --- cpp/include/cudf/operators/error.hpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/cpp/include/cudf/operators/error.hpp b/cpp/include/cudf/operators/error.hpp index 19d27d5dee0d..e29d85fc2948 100644 --- a/cpp/include/cudf/operators/error.hpp +++ b/cpp/include/cudf/operators/error.hpp @@ -4,9 +4,11 @@ */ #pragma once +#include #include #include +#include #include namespace CUDF_EXPORT cudf { @@ -35,21 +37,21 @@ struct result { * * @param value The value of the result. */ - __device__ __host__ constexpr result(T value) : error_(errc::SUCCESS), value_(value) {} + CUDF_HOST_DEVICE constexpr result(T value) : error_(errc::SUCCESS), value_(value) {} /** * @brief Constructs a result with an error. * * @param error The error code of the result. */ - __device__ __host__ constexpr result(errc error) : error_(error), value_() {} + CUDF_HOST_DEVICE constexpr result(errc error) : error_(error), value_() {} /** * @brief Checks if the result has an error. * * @return true if the result has an error, false otherwise. */ - [[nodiscard]] __device__ __host__ constexpr bool has_error() const + [[nodiscard]] CUDF_HOST_DEVICE constexpr bool has_error() const { return error_ != errc::SUCCESS; } @@ -59,7 +61,7 @@ struct result { * * @return true if the result has a value, false otherwise. */ - [[nodiscard]] __device__ __host__ constexpr bool has_value() const { return !has_error(); } + [[nodiscard]] CUDF_HOST_DEVICE constexpr bool has_value() const { return !has_error(); } /** * @brief Returns true if the result has a value, false otherwise. This operator allows the result @@ -67,7 +69,7 @@ struct result { * * @return true if the result has a value, false otherwise. */ - [[nodiscard]] __device__ __host__ constexpr explicit operator bool() const { return has_value(); } + [[nodiscard]] CUDF_HOST_DEVICE constexpr explicit operator bool() const { return has_value(); } /** * @brief Returns the value of the result. Behaviour is undefined if the result has an error (i.e. @@ -75,7 +77,18 @@ struct result { * * @return The value of the result. */ - __device__ __host__ constexpr T const& value() const { return value_; } + CUDF_HOST_DEVICE constexpr T const& value() const { return value_; } + + /** + * @brief Returns the value of the result (if successful) or null (if it has an error value). + * + * @return The value of the result or null. + */ + CUDF_HOST_DEVICE constexpr cuda::std::optional value_or_null() const + { + if (error_ != errc::SUCCESS) { return {}; } + return value_; + } /** * @brief Returns the error code of the result. Behaviour is undefined if the result has a value @@ -83,7 +96,7 @@ struct result { * * @return The error code of the result. */ - [[nodiscard]] __device__ __host__ constexpr errc error() const { return error_; } + [[nodiscard]] CUDF_HOST_DEVICE constexpr errc error() const { return error_; } }; // Helper variable template to detect if a type is a result type From 87fe99af3a344af93ead81c036aa76fcf93db8f8 Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Mon, 1 Jun 2026 18:26:43 +0100 Subject: [PATCH 33/34] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- cpp/include/cudf/operators/error.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/include/cudf/operators/error.hpp b/cpp/include/cudf/operators/error.hpp index e29d85fc2948..1d06d301fdbc 100644 --- a/cpp/include/cudf/operators/error.hpp +++ b/cpp/include/cudf/operators/error.hpp @@ -9,7 +9,6 @@ #include #include -#include namespace CUDF_EXPORT cudf { namespace ops { From 2f62ba3be085f6bf52a3c43df10d99b66b162a5f Mon Sep 17 00:00:00 2001 From: Basit Ayantunde Date: Tue, 2 Jun 2026 00:45:24 +0000 Subject: [PATCH 34/34] refactoring and cleanup --- .../cudf/ast/detail/operator_functor.cuh | 32 ++--- .../operators/ansi_arithmetic.cuh | 115 ++++++++++-------- .../{ => detail}/operators/arithmetic.cuh | 5 +- .../cudf/{ => detail}/operators/bitwise.cuh | 5 +- .../cudf/{ => detail}/operators/casts.cuh | 5 +- .../{ => detail}/operators/comparison.cuh | 15 +-- .../cudf/{ => detail}/operators/concepts.cuh | 5 +- .../types.cuh => detail/operators/error.hpp} | 10 +- .../cudf/{ => detail}/operators/identity.cuh | 2 + .../cudf/{ => detail}/operators/logic.cuh | 99 ++++++++------- .../cudf/{ => detail}/operators/math.cuh | 42 ++++--- .../{ => detail}/operators/null_handling.cuh | 18 ++- .../cudf/detail/operators/operators.cuh | 18 +++ .../{ => detail}/operators/trigonometric.cuh | 5 +- cpp/include/cudf/operators/all.cuh | 18 --- cpp/include/cudf/operators/error.hpp | 110 ----------------- cpp/src/jit/row_ir.cpp | 2 +- cpp/tests/jit/row_ir.cpp | 4 +- 18 files changed, 215 insertions(+), 295 deletions(-) rename cpp/include/cudf/{ => detail}/operators/ansi_arithmetic.cuh (54%) rename cpp/include/cudf/{ => detail}/operators/arithmetic.cuh (97%) rename cpp/include/cudf/{ => detail}/operators/bitwise.cuh (95%) rename cpp/include/cudf/{ => detail}/operators/casts.cuh (98%) rename cpp/include/cudf/{ => detail}/operators/comparison.cuh (89%) rename cpp/include/cudf/{ => detail}/operators/concepts.cuh (91%) rename cpp/include/cudf/{operators/types.cuh => detail/operators/error.hpp} (52%) rename cpp/include/cudf/{ => detail}/operators/identity.cuh (91%) rename cpp/include/cudf/{ => detail}/operators/logic.cuh (79%) rename cpp/include/cudf/{ => detail}/operators/math.cuh (81%) rename cpp/include/cudf/{ => detail}/operators/null_handling.cuh (79%) create mode 100644 cpp/include/cudf/detail/operators/operators.cuh rename cpp/include/cudf/{ => detail}/operators/trigonometric.cuh (96%) delete mode 100644 cpp/include/cudf/operators/all.cuh delete mode 100644 cpp/include/cudf/operators/error.hpp diff --git a/cpp/include/cudf/ast/detail/operator_functor.cuh b/cpp/include/cudf/ast/detail/operator_functor.cuh index fe487f4b72dd..2d600247b41b 100644 --- a/cpp/include/cudf/ast/detail/operator_functor.cuh +++ b/cpp/include/cudf/ast/detail/operator_functor.cuh @@ -5,7 +5,7 @@ #pragma once #include -#include +#include #include #include #include @@ -19,15 +19,15 @@ namespace ast::detail { template struct operator_invoker; -#define CUDF_AST_OPERATOR_MAP(OP, func_name, num_args) \ - template <> \ - struct operator_invoker { \ - static constexpr auto arity = num_args; \ - template \ - __device__ static inline auto eval(Args... a) -> decltype(cudf::ops::func_name(a...)) \ - { \ - return cudf::ops::func_name(a...); \ - } \ +#define CUDF_AST_OPERATOR_MAP(OP, func_name, num_args) \ + template <> \ + struct operator_invoker { \ + static constexpr auto arity = num_args; \ + template \ + __device__ static inline auto eval(Args... a) -> decltype(cudf::detail::ops::func_name(a...)) \ + { \ + return cudf::detail::ops::func_name(a...); \ + } \ }; CUDF_AST_OPERATOR_MAP(ADD, add, 2) @@ -103,7 +103,7 @@ struct operator_functor { template __device__ inline auto operator()(T a) - requires(!cudf::ops::nullable && requires { operator_invoker::eval(a); }) + requires(!cudf::detail::ops::nullable && requires { operator_invoker::eval(a); }) { return operator_invoker::eval(a); } @@ -111,7 +111,7 @@ struct operator_functor { template __device__ inline auto operator()(T a) requires( - cudf::ops::nullable && (requires { operator_invoker::eval(a); } || + cudf::detail::ops::nullable && (requires { operator_invoker::eval(a); } || requires { operator_invoker::eval(a.value()); }) ) { @@ -120,7 +120,7 @@ struct operator_functor { if constexpr (requires { operator_invoker::eval(a); }) { return operator_invoker::eval(a); } else { - using result_t = cudf::ops::optional::eval(a.value()))>; + using result_t = cuda::std::optional::eval(a.value()))>; if (a.has_value()) { return result_t{operator_invoker::eval(a.value())}; } else { @@ -131,7 +131,7 @@ struct operator_functor { template __device__ inline auto operator()(T a, T b) - requires(!cudf::ops::nullable && requires { operator_invoker::eval(a, b); }) + requires(!cudf::detail::ops::nullable && requires { operator_invoker::eval(a, b); }) { return operator_invoker::eval(a, b); } @@ -139,7 +139,7 @@ struct operator_functor { template __device__ inline auto operator()(T a, T b) requires( - cudf::ops::nullable && + cudf::detail::ops::nullable && (requires { operator_invoker::eval(a, b); } || requires { operator_invoker::eval(a.value(), b.value()); })) { @@ -149,7 +149,7 @@ struct operator_functor { return operator_invoker::eval(a, b); } else { using result_t = - cudf::ops::optional::eval(a.value(), b.value()))>; + cuda::std::optional::eval(a.value(), b.value()))>; if (a.has_value() && b.has_value()) { return result_t{operator_invoker::eval(a.value(), b.value())}; } else { diff --git a/cpp/include/cudf/operators/ansi_arithmetic.cuh b/cpp/include/cudf/detail/operators/ansi_arithmetic.cuh similarity index 54% rename from cpp/include/cudf/operators/ansi_arithmetic.cuh rename to cpp/include/cudf/detail/operators/ansi_arithmetic.cuh index c10b5f2e181c..1187eb4b44bf 100644 --- a/cpp/include/cudf/operators/ansi_arithmetic.cuh +++ b/cpp/include/cudf/detail/operators/ansi_arithmetic.cuh @@ -4,19 +4,20 @@ */ #pragma once +#include +#include +#include #include -#include -#include -#include -#include #include #include #include +#include #include #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { /** @@ -28,15 +29,15 @@ namespace ops { * @return `errc::OVERFLOW` on overflow, else the result. */ template -__device__ result ansi_add(T a, T b) +__device__ cuda::std::expected ansi_add(T a, T b) { T r; - if (cuda::add_overflow(r, a, b).overflow) { return errc::OVERFLOW; } + if (cuda::add_overflow(r, a, b).overflow) { return cuda::std::unexpected{errc::OVERFLOW}; } return r; } template -__device__ result ansi_add(T a, T b) +__device__ cuda::std::expected ansi_add(T a, T b) { return a + b; } @@ -48,12 +49,13 @@ __device__ result ansi_add(T a, T b) * @return `errc::OVERFLOW` on overflow, else the result. */ template -__device__ result> ansi_add(numeric::decimal a, numeric::decimal b) +__device__ cuda::std::expected, errc> ansi_add(numeric::decimal a, + numeric::decimal b) { auto scale = cuda::std::min(a.scale(), b.scale()); if (numeric::addition_overflow(a.rescaled(scale).value(), b.rescaled(scale).value())) { - return errc::OVERFLOW; + return cuda::std::unexpected{errc::OVERFLOW}; } return numeric::decimal{numeric::scaled_integer{ @@ -61,10 +63,10 @@ __device__ result> ansi_add(numeric::decimal a, numeric:: } template -__device__ result ansi_sub(T a, T b) +__device__ cuda::std::expected ansi_sub(T a, T b) { T r; - if (cuda::sub_overflow(r, a, b).overflow) { return errc::OVERFLOW; } + if (cuda::sub_overflow(r, a, b).overflow) { return cuda::std::unexpected{errc::OVERFLOW}; } return r; } @@ -75,18 +77,19 @@ __device__ result ansi_sub(T a, T b) * @return `errc::OVERFLOW` on overflow, else the result. */ template -__device__ result ansi_sub(T a, T b) +__device__ cuda::std::expected ansi_sub(T a, T b) { return a - b; } template -__device__ result> ansi_sub(numeric::decimal a, numeric::decimal b) +__device__ cuda::std::expected, errc> ansi_sub(numeric::decimal a, + numeric::decimal b) { auto scale = cuda::std::min(a.scale(), b.scale()); if (numeric::subtraction_overflow(a.rescaled(scale).value(), b.rescaled(scale).value())) { - return errc::OVERFLOW; + return cuda::std::unexpected{errc::OVERFLOW}; } return numeric::decimal{numeric::scaled_integer{ @@ -102,23 +105,26 @@ __device__ result> ansi_sub(numeric::decimal a, numeric:: * @return `errc::OVERFLOW` on overflow, else the result. */ template -__device__ result ansi_mul(T a, T b) +__device__ cuda::std::expected ansi_mul(T a, T b) { T r; - if (cuda::mul_overflow(r, a, b).overflow) { return errc::OVERFLOW; } + if (cuda::mul_overflow(r, a, b).overflow) { return cuda::std::unexpected{errc::OVERFLOW}; } return r; } template -__device__ result ansi_mul(T a, T b) +__device__ cuda::std::expected ansi_mul(T a, T b) { return a * b; } template -__device__ result> ansi_mul(numeric::decimal a, numeric::decimal b) +__device__ cuda::std::expected, errc> ansi_mul(numeric::decimal a, + numeric::decimal b) { - if (numeric::multiplication_overflow(a.value(), b.value())) { return errc::OVERFLOW; } + if (numeric::multiplication_overflow(a.value(), b.value())) { + return cuda::std::unexpected{errc::OVERFLOW}; + } return numeric::decimal{ numeric::scaled_integer{a.value() * b.value(), numeric::scale_type{a.scale() + b.scale()}}}; @@ -134,26 +140,29 @@ __device__ result> ansi_mul(numeric::decimal a, numeric:: * `errc::SUCCESS`. */ template -__device__ result ansi_div(T a, T b) +__device__ cuda::std::expected ansi_div(T a, T b) { - if (b == 0) { return errc::DIVISION_BY_ZERO; } + if (b == 0) { return cuda::std::unexpected{errc::DIVISION_BY_ZERO}; } T r; - if (cuda::div_overflow(r, a, b).overflow) { return errc::OVERFLOW; } + if (cuda::div_overflow(r, a, b).overflow) { return cuda::std::unexpected{errc::OVERFLOW}; } return r; } template -__device__ result ansi_div(T a, T b) +__device__ cuda::std::expected ansi_div(T a, T b) { return a / b; } template -__device__ result> ansi_div(numeric::decimal a, numeric::decimal b) +__device__ cuda::std::expected, errc> ansi_div(numeric::decimal a, + numeric::decimal b) { - if (b.value() == 0) { return errc::DIVISION_BY_ZERO; } + if (b.value() == 0) { return cuda::std::unexpected{errc::DIVISION_BY_ZERO}; } - if (numeric::division_overflow(a.value(), b.value())) { return errc::OVERFLOW; } + if (numeric::division_overflow(a.value(), b.value())) { + return cuda::std::unexpected{errc::OVERFLOW}; + } return numeric::decimal{ numeric::scaled_integer{a.value() / b.value(), numeric::scale_type{a.scale() - b.scale()}}}; @@ -168,9 +177,9 @@ __device__ result> ansi_div(numeric::decimal a, numeric:: * @return `errc::DIVISION_BY_ZERO` on zero divisor, else the result. */ template -__device__ result ansi_mod(T a, T b) +__device__ cuda::std::expected ansi_mod(T a, T b) { - if (b == 0) { return errc::DIVISION_BY_ZERO; } + if (b == 0) { return cuda::std::unexpected{errc::DIVISION_BY_ZERO}; } // avoid signed overflow UB / trap for minimum value divided by -1. if (a == cuda::std::numeric_limits::min() && b == T{-1}) { return T{0}; } @@ -179,21 +188,22 @@ __device__ result ansi_mod(T a, T b) } template -__device__ result ansi_mod(T a, T b) +__device__ cuda::std::expected ansi_mod(T a, T b) { - if (b == 0) { return errc::DIVISION_BY_ZERO; } + if (b == 0) { return cuda::std::unexpected{errc::DIVISION_BY_ZERO}; } return a % b; } template -__device__ inline result ansi_mod(T a, T b) +__device__ cuda::std::expected ansi_mod(T a, T b) { - if (b == 0) { return errc::DIVISION_BY_ZERO; } + if (b == 0) { return cuda::std::unexpected{errc::DIVISION_BY_ZERO}; } return a - b * cuda::std::floor(a / b); } template -__device__ result> ansi_mod(numeric::decimal a, numeric::decimal b) +__device__ cuda::std::expected, errc> ansi_mod(numeric::decimal a, + numeric::decimal b) { auto r = ansi_div(a, b); if (r.has_error()) { return r.error(); } @@ -208,28 +218,30 @@ __device__ result> ansi_mod(numeric::decimal a, numeric:: * @return `errc::OVERFLOW` on overflow, else the result. */ template -__device__ result ansi_abs(T a) +__device__ cuda::std::expected ansi_abs(T a) { - if (a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + if (a == cuda::std::numeric_limits::min()) { return cuda::std::unexpected{errc::OVERFLOW}; } return (a < 0) ? -a : a; } template -__device__ result ansi_abs(T a) +__device__ cuda::std::expected ansi_abs(T a) { return a; } template -__device__ result ansi_abs(T a) +__device__ cuda::std::expected ansi_abs(T a) { return cuda::std::fabs(a); } template -__device__ result> ansi_abs(numeric::decimal a) +__device__ cuda::std::expected, errc> ansi_abs(numeric::decimal a) { - if (a.value() == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + if (a.value() == cuda::std::numeric_limits::min()) { + return cuda::std::unexpected{errc::OVERFLOW}; + } auto rep = a.value() < 0 ? -a.value() : a.value(); return numeric::decimal{numeric::scaled_integer{rep, numeric::scale_type{a.scale()}}}; } @@ -242,22 +254,24 @@ __device__ result> ansi_abs(numeric::decimal a) * @return `errc::OVERFLOW` on overflow, else the result. */ template -__device__ result ansi_neg(T a) +__device__ cuda::std::expected ansi_neg(T a) { - if (a == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + if (a == cuda::std::numeric_limits::min()) { return cuda::std::unexpected{errc::OVERFLOW}; } return -a; } template -__device__ result ansi_neg(T a) +__device__ cuda::std::expected ansi_neg(T a) { return -a; } template -__device__ result> ansi_neg(numeric::decimal a) +__device__ cuda::std::expected, errc> ansi_neg(numeric::decimal a) { - if (a.value() == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + if (a.value() == cuda::std::numeric_limits::min()) { + return cuda::std::unexpected{errc::OVERFLOW}; + } auto rep = -a.value(); return numeric::decimal{numeric::scaled_integer{rep, numeric::scale_type{a.scale()}}}; } @@ -271,22 +285,25 @@ __device__ result> ansi_neg(numeric::decimal a) * @return `errc::OVERFLOW` when precision is invalid or exceeded, else the result. */ template -__device__ result> ansi_precision_check(numeric::decimal a, - int32_t precision) +__device__ cuda::std::expected, errc> ansi_precision_check( + numeric::decimal a, int32_t precision) { - if (precision <= 0) { return errc::OVERFLOW; } + if (precision <= 0) { return cuda::std::unexpected{errc::OVERFLOW}; } auto value = a.value(); - if (value == cuda::std::numeric_limits::min()) { return errc::OVERFLOW; } + if (value == cuda::std::numeric_limits::min()) { + return cuda::std::unexpected{errc::OVERFLOW}; + } auto abs_value = value < 0 ? -value : value; if (abs_value >= numeric::detail::ipow(precision)) { - return errc::OVERFLOW; + return cuda::std::unexpected{errc::OVERFLOW}; } return a; } } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/arithmetic.cuh b/cpp/include/cudf/detail/operators/arithmetic.cuh similarity index 97% rename from cpp/include/cudf/operators/arithmetic.cuh rename to cpp/include/cudf/detail/operators/arithmetic.cuh index f73f46338378..b335f6d7de4c 100644 --- a/cpp/include/cudf/operators/arithmetic.cuh +++ b/cpp/include/cudf/detail/operators/arithmetic.cuh @@ -5,9 +5,8 @@ #pragma once #include +#include #include -#include -#include #include #include @@ -15,6 +14,7 @@ #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { /** @@ -193,4 +193,5 @@ __device__ auto true_div(A a, B b) -> decltype(static_cast(a) / static_c } } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/bitwise.cuh b/cpp/include/cudf/detail/operators/bitwise.cuh similarity index 95% rename from cpp/include/cudf/operators/bitwise.cuh rename to cpp/include/cudf/detail/operators/bitwise.cuh index 0b0b560105fb..957c0feb2fdc 100644 --- a/cpp/include/cudf/operators/bitwise.cuh +++ b/cpp/include/cudf/detail/operators/bitwise.cuh @@ -4,11 +4,11 @@ */ #pragma once -#include -#include +#include #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { /** @@ -94,4 +94,5 @@ __device__ auto bit_shift_right(A a, B b) -> decltype(a >> b) } } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/casts.cuh b/cpp/include/cudf/detail/operators/casts.cuh similarity index 98% rename from cpp/include/cudf/operators/casts.cuh rename to cpp/include/cudf/detail/operators/casts.cuh index 0f8c61cc50e9..e6f24fa427c6 100644 --- a/cpp/include/cudf/operators/casts.cuh +++ b/cpp/include/cudf/detail/operators/casts.cuh @@ -4,15 +4,15 @@ */ #pragma once +#include #include #include -#include -#include #include #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { /** @@ -238,4 +238,5 @@ __device__ numeric::decimal rescale(numeric::decimal a, int32_t new_scale) } } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/comparison.cuh b/cpp/include/cudf/detail/operators/comparison.cuh similarity index 89% rename from cpp/include/cudf/operators/comparison.cuh rename to cpp/include/cudf/detail/operators/comparison.cuh index 785e04655eac..362f17039470 100644 --- a/cpp/include/cudf/operators/comparison.cuh +++ b/cpp/include/cudf/detail/operators/comparison.cuh @@ -4,13 +4,13 @@ */ #pragma once -#include -#include +#include #include #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { /** @@ -119,17 +119,12 @@ __device__ bool null_equal(A a, B b) } template -__device__ bool null_equal(optional a, optional b) +__device__ bool null_equal(cuda::std::optional a, cuda::std::optional b) requires(!nullable && !nullable && requires { a == b; }) { - if (a.has_value() && b.has_value()) { - return null_equal(a.value(), b.value()); - } else if (!a.has_value() && !b.has_value()) { - return true; - } else { - return false; - } + return a == b; } } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/concepts.cuh b/cpp/include/cudf/detail/operators/concepts.cuh similarity index 91% rename from cpp/include/cudf/operators/concepts.cuh rename to cpp/include/cudf/detail/operators/concepts.cuh index 47b6052d6333..33b56d24067b 100644 --- a/cpp/include/cudf/operators/concepts.cuh +++ b/cpp/include/cudf/detail/operators/concepts.cuh @@ -4,7 +4,6 @@ */ #pragma once -#include #include #include @@ -13,6 +12,7 @@ #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { template @@ -35,10 +35,11 @@ template constexpr bool is_nullable = false; template -constexpr bool is_nullable> = true; +constexpr bool is_nullable> = true; template concept nullable = is_nullable; } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/types.cuh b/cpp/include/cudf/detail/operators/error.hpp similarity index 52% rename from cpp/include/cudf/operators/types.cuh rename to cpp/include/cudf/detail/operators/error.hpp index c349849027a6..d43a8f6302c9 100644 --- a/cpp/include/cudf/operators/types.cuh +++ b/cpp/include/cudf/detail/operators/error.hpp @@ -6,13 +6,17 @@ #include -#include +#include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { -template -using optional = cuda::std::optional; +/** + * @brief An enumeration of error codes that can occur during operations. + */ +enum class errc : cuda::std::int8_t { SUCCESS = 0, OVERFLOW = 1, DIVISION_BY_ZERO = 2 }; } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/identity.cuh b/cpp/include/cudf/detail/operators/identity.cuh similarity index 91% rename from cpp/include/cudf/operators/identity.cuh rename to cpp/include/cudf/detail/operators/identity.cuh index 0a8b4b72f2a5..b14c98b8bea9 100644 --- a/cpp/include/cudf/operators/identity.cuh +++ b/cpp/include/cudf/detail/operators/identity.cuh @@ -7,6 +7,7 @@ #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { /** @@ -22,4 +23,5 @@ __device__ T identity(T a) } } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/logic.cuh b/cpp/include/cudf/detail/operators/logic.cuh similarity index 79% rename from cpp/include/cudf/operators/logic.cuh rename to cpp/include/cudf/detail/operators/logic.cuh index 52467431aefa..e9fa3890c55d 100644 --- a/cpp/include/cudf/operators/logic.cuh +++ b/cpp/include/cudf/detail/operators/logic.cuh @@ -4,15 +4,55 @@ */ #pragma once -#include -#include +#include #include #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { +/** + * @brief Computes logical AND. + * + * @tparam A Left operand type. + * @tparam B Right operand type. + * @param a Left operand. + * @param b Right operand. + */ +template +__device__ bool logical_and(A a, B b) +{ + return a && b; +} + +/** + * @brief Computes logical OR. + * + * @tparam A Left operand type. + * @tparam B Right operand type. + * @param a Left operand. + * @param b Right operand. + */ +template +__device__ bool logical_or(A a, B b) +{ + return a || b; +} + +/** + * @brief Computes logical NOT. + * + * @tparam T Value type. + * @param a Input operand. + */ +template +__device__ bool logical_not(T a) +{ + return !a; +} + /** * @brief Computes logical AND with null-aware semantics. * @@ -24,11 +64,12 @@ namespace ops { template __device__ bool null_logical_and(A a, B b) { - return a && b; + return logical_and(a, b); } template -__device__ optional null_logical_and(optional a, optional b) +__device__ cuda::std::optional null_logical_and(cuda::std::optional a, + cuda::std::optional b) { if (a.has_value() && b.has_value()) { return null_logical_and(a.value(), b.value()); @@ -54,11 +95,12 @@ __device__ optional null_logical_and(optional a, optional b) template __device__ bool null_logical_or(A a, B b) { - return a || b; + return logical_or(a, b); } template -__device__ optional null_logical_or(optional a, optional b) +__device__ cuda::std::optional null_logical_or(cuda::std::optional a, + cuda::std::optional b) { if (a.has_value() && b.has_value()) { return null_logical_or(a.value(), b.value()); @@ -73,46 +115,6 @@ __device__ optional null_logical_or(optional a, optional b) } } -/** - * @brief Computes logical AND. - * - * @tparam A Left operand type. - * @tparam B Right operand type. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ bool logical_and(A a, B b) -{ - return a && b; -} - -/** - * @brief Computes logical OR. - * - * @tparam A Left operand type. - * @tparam B Right operand type. - * @param a Left operand. - * @param b Right operand. - */ -template -__device__ bool logical_or(A a, B b) -{ - return a || b; -} - -/** - * @brief Computes logical NOT. - * - * @tparam T Value type. - * @param a Input operand. - */ -template -__device__ bool logical_not(T a) -{ - return !a; -} - /** * @brief Selects one of two values based on a predicate. * @@ -128,10 +130,13 @@ __device__ T if_else(T true_value, T false_value, bool pred) } template -__device__ optional if_else(optional true_value, optional false_value, optional pred) +__device__ cuda::std::optional if_else(cuda::std::optional true_value, + cuda::std::optional false_value, + cuda::std::optional pred) { return pred.value_or(false) ? true_value : false_value; } } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/math.cuh b/cpp/include/cudf/detail/operators/math.cuh similarity index 81% rename from cpp/include/cudf/operators/math.cuh rename to cpp/include/cudf/detail/operators/math.cuh index d1cb1320bd3a..e33c6b022499 100644 --- a/cpp/include/cudf/operators/math.cuh +++ b/cpp/include/cudf/detail/operators/math.cuh @@ -5,15 +5,15 @@ #pragma once #include +#include #include -#include -#include #include #include #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { /** @@ -40,8 +40,16 @@ __device__ T ceil(T a) return cuda::std::ceil(a); } -template -__device__ numeric::decimal ceil(numeric::decimal a) +namespace detail { + +/** + * @brief Rounds a decimal value to an integral value. + * + * @tparam R Rep type of the decimal. + * @tparam ceil If true `ceil`s the value, otherwise `floor`s the value. + */ +template +__device__ numeric::decimal decimal_round(numeric::decimal a) { if (a.scale() >= 0) { return a; @@ -53,12 +61,20 @@ __device__ numeric::decimal ceil(numeric::decimal a) if (rem == 0) { return a; } else { - auto val = a.value() > 0 ? (div + 1) : div; + auto val = ceil ? (a.value() > 0 ? (div + 1) : div) : (a.value() > 0 ? div : (div - 1)); return numeric::decimal{numeric::scaled_integer{val * factor, a.scale()}}; } } } +} // namespace detail + +template +__device__ numeric::decimal ceil(numeric::decimal a) +{ + return detail::decimal_round(a); +} + /** * @brief Computes natural exponential. * @@ -86,20 +102,7 @@ __device__ T floor(T a) template __device__ numeric::decimal floor(numeric::decimal a) { - if (a.scale() >= 0) { - return a; - } else { - auto factor = - numeric::detail::ipow(-static_cast(a.scale())); - auto div = a.value() / factor; - auto rem = a.value() % factor; - if (rem == 0) { - return a; - } else { - auto val = a.value() > 0 ? div : (div - 1); - return numeric::decimal{numeric::scaled_integer{val * factor, a.scale()}}; - } - } + return detail::decimal_round(a); } /** @@ -158,4 +161,5 @@ __device__ T sqrt(T a) } } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/null_handling.cuh b/cpp/include/cudf/detail/operators/null_handling.cuh similarity index 79% rename from cpp/include/cudf/operators/null_handling.cuh rename to cpp/include/cudf/detail/operators/null_handling.cuh index 0e3b7278f182..e26331b72e1e 100644 --- a/cpp/include/cudf/operators/null_handling.cuh +++ b/cpp/include/cudf/detail/operators/null_handling.cuh @@ -5,13 +5,13 @@ #pragma once -#include -#include +#include #include #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { /** @@ -42,7 +42,8 @@ __device__ bool is_null(T a) * @param condition boolean condition. */ template -__device__ optional nullify_if(optional a, optional condition) +__device__ cuda::std::optional nullify_if(cuda::std::optional a, + cuda::std::optional condition) { if (condition.has_value() && a.has_value()) { if (condition.value()) { @@ -70,7 +71,7 @@ __device__ T coalesce(T a, T b) } template -__device__ optional coalesce(optional a, optional b) +__device__ cuda::std::optional coalesce(cuda::std::optional a, cuda::std::optional b) { if (a.has_value()) { return a.value(); @@ -93,14 +94,11 @@ __device__ inline bool predicate(T a) } template T> -__device__ inline bool predicate(optional a) +__device__ inline bool predicate(cuda::std::optional a) { - if (a.has_value()) { - return a.value(); - } else { - return false; - } + return a.value_or(false); } } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/detail/operators/operators.cuh b/cpp/include/cudf/detail/operators/operators.cuh new file mode 100644 index 000000000000..b52055d0a6b4 --- /dev/null +++ b/cpp/include/cudf/detail/operators/operators.cuh @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/cpp/include/cudf/operators/trigonometric.cuh b/cpp/include/cudf/detail/operators/trigonometric.cuh similarity index 96% rename from cpp/include/cudf/operators/trigonometric.cuh rename to cpp/include/cudf/detail/operators/trigonometric.cuh index 19e10d475611..1d3274889227 100644 --- a/cpp/include/cudf/operators/trigonometric.cuh +++ b/cpp/include/cudf/detail/operators/trigonometric.cuh @@ -4,14 +4,14 @@ */ #pragma once -#include -#include +#include #include #include #include namespace CUDF_EXPORT cudf { +namespace detail { namespace ops { /** @@ -159,4 +159,5 @@ __device__ T tanh(T a) } } // namespace ops +} // namespace detail } // namespace CUDF_EXPORT cudf diff --git a/cpp/include/cudf/operators/all.cuh b/cpp/include/cudf/operators/all.cuh deleted file mode 100644 index 2ebd187073aa..000000000000 --- a/cpp/include/cudf/operators/all.cuh +++ /dev/null @@ -1,18 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/cpp/include/cudf/operators/error.hpp b/cpp/include/cudf/operators/error.hpp deleted file mode 100644 index 1d06d301fdbc..000000000000 --- a/cpp/include/cudf/operators/error.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. - * SPDX-License-Identifier: Apache-2.0 - */ -#pragma once - -#include -#include - -#include -#include - -namespace CUDF_EXPORT cudf { -namespace ops { - -/** - * @brief An enumeration of error codes that can occur during operations. - */ -enum class errc : cuda::std::int8_t { SUCCESS = 0, OVERFLOW = 1, DIVISION_BY_ZERO = 2 }; - -/** - * @brief A type that represents the result of an operation, which can either be a value or an - * error. - * - * @tparam T The type of the value. - */ -template -struct result { - private: - errc error_; //< The error code of the result, errc::SUCCESS if the operation was successful - T value_; //< The value of the result, only valid if error_ is errc::SUCCESS - - public: - /** - * @brief Constructs a result with a value. - * - * @param value The value of the result. - */ - CUDF_HOST_DEVICE constexpr result(T value) : error_(errc::SUCCESS), value_(value) {} - - /** - * @brief Constructs a result with an error. - * - * @param error The error code of the result. - */ - CUDF_HOST_DEVICE constexpr result(errc error) : error_(error), value_() {} - - /** - * @brief Checks if the result has an error. - * - * @return true if the result has an error, false otherwise. - */ - [[nodiscard]] CUDF_HOST_DEVICE constexpr bool has_error() const - { - return error_ != errc::SUCCESS; - } - - /** - * @brief Checks if the result has a value. - * - * @return true if the result has a value, false otherwise. - */ - [[nodiscard]] CUDF_HOST_DEVICE constexpr bool has_value() const { return !has_error(); } - - /** - * @brief Returns true if the result has a value, false otherwise. This operator allows the result - * to be used in boolean contexts, such as if statements. - * - * @return true if the result has a value, false otherwise. - */ - [[nodiscard]] CUDF_HOST_DEVICE constexpr explicit operator bool() const { return has_value(); } - - /** - * @brief Returns the value of the result. Behaviour is undefined if the result has an error (i.e. - * value() is called on a result that has an error). - * - * @return The value of the result. - */ - CUDF_HOST_DEVICE constexpr T const& value() const { return value_; } - - /** - * @brief Returns the value of the result (if successful) or null (if it has an error value). - * - * @return The value of the result or null. - */ - CUDF_HOST_DEVICE constexpr cuda::std::optional value_or_null() const - { - if (error_ != errc::SUCCESS) { return {}; } - return value_; - } - - /** - * @brief Returns the error code of the result. Behaviour is undefined if the result has a value - * (i.e. error() is called on a result that does not have an error). - * - * @return The error code of the result. - */ - [[nodiscard]] CUDF_HOST_DEVICE constexpr errc error() const { return error_; } -}; - -// Helper variable template to detect if a type is a result type -template -constexpr bool is_result = false; - -// Specialization for result types -template -constexpr bool is_result> = true; - -} // namespace ops -} // namespace CUDF_EXPORT cudf diff --git a/cpp/src/jit/row_ir.cpp b/cpp/src/jit/row_ir.cpp index 120df6638a44..25861c7c6578 100644 --- a/cpp/src/jit/row_ir.cpp +++ b/cpp/src/jit/row_ir.cpp @@ -370,7 +370,7 @@ void node::emit_code(instance_context& instance, target_info const& info, code_s if (op_ == opcode::PREDICATE) { sink.emit(std::format( - R"***(bool {} = cudf::ops::predicate({}); + R"***(bool {} = cudf::detail::ops::predicate({}); )***", id_, args_str)); diff --git a/cpp/tests/jit/row_ir.cpp b/cpp/tests/jit/row_ir.cpp index 551f89f1a5aa..a684077a2be2 100644 --- a/cpp/tests/jit/row_ir.cpp +++ b/cpp/tests/jit/row_ir.cpp @@ -352,7 +352,7 @@ TEST_F(RowIRCudaCodeGenTest, FilterPredicate) filter_predicate.emit_code(ctx, target_info, sink); auto expected_code = R"***(bool tmp_0 = in_0; -bool tmp_1 = cudf::ops::predicate(tmp_0); +bool tmp_1 = cudf::detail::ops::predicate(tmp_0); )***"; EXPECT_EQ(sink.get_code(), expected_code); @@ -370,7 +370,7 @@ bool tmp_1 = cudf::ops::predicate(tmp_0); filter_predicate.emit_code(ctx, target_info, sink); auto expected_code = R"***(cuda::std::optional tmp_0 = in_0; -bool tmp_1 = cudf::ops::predicate(tmp_0); +bool tmp_1 = cudf::detail::ops::predicate(tmp_0); )***"; EXPECT_EQ(sink.get_code(), expected_code);