From ff007b7e6a49dfd77061ca3d923a89ff04cf4039 Mon Sep 17 00:00:00 2001 From: David Li Date: Mon, 21 Jun 2021 13:11:05 -0400 Subject: [PATCH 1/4] ARROW-13096: [C++] Implement logarithm compute functions --- cpp/src/arrow/compute/api_scalar.cc | 4 + cpp/src/arrow/compute/api_scalar.h | 46 ++++ .../compute/kernels/scalar_arithmetic.cc | 204 +++++++++++++++++- .../compute/kernels/scalar_arithmetic_test.cc | 52 +++++ docs/source/cpp/compute.rst | 26 +++ docs/source/python/api/compute.rst | 18 ++ 6 files changed, 346 insertions(+), 4 deletions(-) diff --git a/cpp/src/arrow/compute/api_scalar.cc b/cpp/src/arrow/compute/api_scalar.cc index 2021c8a30c6..719fbed78d5 100644 --- a/cpp/src/arrow/compute/api_scalar.cc +++ b/cpp/src/arrow/compute/api_scalar.cc @@ -326,6 +326,10 @@ SCALAR_ARITHMETIC_UNARY(Asin, "asin", "asin_checked") SCALAR_ARITHMETIC_UNARY(Acos, "acos", "acos_checked") SCALAR_ARITHMETIC_UNARY(Tan, "tan", "tan_checked") SCALAR_EAGER_UNARY(Atan, "atan") +SCALAR_ARITHMETIC_UNARY(Ln, "ln", "ln_checked") +SCALAR_ARITHMETIC_UNARY(Log10, "log10", "log10_checked") +SCALAR_ARITHMETIC_UNARY(Log2, "log2", "log2_checked") +SCALAR_ARITHMETIC_UNARY(Log1p, "log1p", "log1p_checked") #define SCALAR_ARITHMETIC_BINARY(NAME, REGISTRY_NAME, REGISTRY_CHECKED_NAME) \ Result NAME(const Datum& left, const Datum& right, ArithmeticOptions options, \ diff --git a/cpp/src/arrow/compute/api_scalar.h b/cpp/src/arrow/compute/api_scalar.h index 89b4faca940..43fcbf6928a 100644 --- a/cpp/src/arrow/compute/api_scalar.h +++ b/cpp/src/arrow/compute/api_scalar.h @@ -424,6 +424,52 @@ Result Atan(const Datum& arg, ExecContext* ctx = NULLPTR); ARROW_EXPORT Result Atan2(const Datum& y, const Datum& x, ExecContext* ctx = NULLPTR); +/// \brief Get the natural log of a value. Array values can be of arbitrary +/// length. If argument is null the result will be null. +/// +/// \param[in] arg the value transformed +/// \param[in] options arithmetic options (overflow handling), optional +/// \param[in] ctx the function execution context, optional +/// \return the elementwise natural log +ARROW_EXPORT +Result Ln(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(), + ExecContext* ctx = NULLPTR); + +/// \brief Get the log base 10 of a value. Array values can be of arbitrary +/// length. If argument is null the result will be null. +/// +/// \param[in] arg the value transformed +/// \param[in] options arithmetic options (overflow handling), optional +/// \param[in] ctx the function execution context, optional +/// \return the elementwise log base 10 +ARROW_EXPORT +Result Log10(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(), + ExecContext* ctx = NULLPTR); + +/// \brief Get the log base 2 of a value. Array values can be of arbitrary +/// length. If argument is null the result will be null. +/// +/// \param[in] arg the value transformed +/// \param[in] options arithmetic options (overflow handling), optional +/// \param[in] ctx the function execution context, optional +/// \return the elementwise log base 2 +ARROW_EXPORT +Result Log2(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(), + ExecContext* ctx = NULLPTR); + +/// \brief Get the natural log of (1 + value). Array values can be of arbitrary +/// length. If argument is null the result will be null. +/// +/// This function may be more accurate than Log(1 + value) for values close to zero. +/// +/// \param[in] arg the value transformed +/// \param[in] options arithmetic options (overflow handling), optional +/// \param[in] ctx the function execution context, optional +/// \return the elementwise natural log +ARROW_EXPORT +Result Log1p(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(), + ExecContext* ctx = NULLPTR); + /// \brief Find the element-wise maximum of any number of arrays or scalars. /// Array values must be the same length. /// diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc index da3a3095041..d5a93d958b2 100644 --- a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc +++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc @@ -53,11 +53,11 @@ template using is_signed_integer = std::integral_constant::value && std::is_signed::value>; -template -using enable_if_signed_integer = enable_if_t::value, T>; +template +using enable_if_signed_integer = enable_if_t::value, R>; -template -using enable_if_unsigned_integer = enable_if_t::value, T>; +template +using enable_if_unsigned_integer = enable_if_t::value, R>; template using enable_if_integer = @@ -686,6 +686,118 @@ struct Atan2 { } }; +struct LogNatural { + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) { + static_assert(std::is_same::value, ""); + if (arg == 0.0) { + return -std::numeric_limits::infinity(); + } else if (arg < 0.0) { + return std::numeric_limits::quiet_NaN(); + } + return std::log(arg); + } +}; + +struct LogNaturalChecked { + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) { + static_assert(std::is_same::value, ""); + if (arg == 0.0) { + *st = Status::Invalid("divide by zero"); + return arg; + } else if (arg < 0.0) { + *st = Status::Invalid("domain error"); + return arg; + } + return std::log(arg); + } +}; + +struct Log10 { + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) { + static_assert(std::is_same::value, ""); + if (arg == 0.0) { + return -std::numeric_limits::infinity(); + } else if (arg < 0.0) { + return std::numeric_limits::quiet_NaN(); + } + return std::log10(arg); + } +}; + +struct Log10Checked { + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) { + static_assert(std::is_same::value, ""); + if (arg == 0) { + *st = Status::Invalid("divide by zero"); + return arg; + } else if (arg < 0) { + *st = Status::Invalid("domain error"); + return arg; + } + return std::log10(arg); + } +}; + +struct Log2 { + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) { + static_assert(std::is_same::value, ""); + if (arg == 0.0) { + return -std::numeric_limits::infinity(); + } else if (arg < 0.0) { + return std::numeric_limits::quiet_NaN(); + } + return std::log2(arg); + } +}; + +struct Log2Checked { + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) { + static_assert(std::is_same::value, ""); + if (arg == 0.0) { + *st = Status::Invalid("divide by zero"); + return arg; + } else if (arg < 0.0) { + *st = Status::Invalid("domain error"); + return arg; + } + return std::log2(arg); + } +}; + +struct Log1p { + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) { + static_assert(std::is_same::value, ""); + if (arg == -1) { + return -std::numeric_limits::infinity(); + } else if (arg < -1) { + return std::numeric_limits::quiet_NaN(); + } + return std::log1p(arg); + } +}; + +struct Log1pChecked { + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) { + static_assert(std::is_same::value, ""); + if (arg == -1) { + *st = Status::Invalid("divide by zero"); + return arg; + } else if (arg < -1) { + *st = Status::Invalid("domain error"); + return arg; + } + return std::log1p(arg); + } +}; + // Generate a kernel given an arithmetic functor template