diff --git a/cpp/src/arrow/compute/api_scalar.cc b/cpp/src/arrow/compute/api_scalar.cc index f00fad29d7697..61a16f5f5eb9b 100644 --- a/cpp/src/arrow/compute/api_scalar.cc +++ b/cpp/src/arrow/compute/api_scalar.cc @@ -732,20 +732,26 @@ void RegisterScalarOptions(FunctionRegistry* registry) { SCALAR_ARITHMETIC_UNARY(AbsoluteValue, "abs", "abs_checked") SCALAR_ARITHMETIC_UNARY(Acos, "acos", "acos_checked") +SCALAR_ARITHMETIC_UNARY(Acosh, "acosh", "acosh_checked") SCALAR_ARITHMETIC_UNARY(Asin, "asin", "asin_checked") +SCALAR_ARITHMETIC_UNARY(Atanh, "atanh", "atanh_checked") SCALAR_ARITHMETIC_UNARY(Cos, "cos", "cos_checked") SCALAR_ARITHMETIC_UNARY(Ln, "ln", "ln_checked") SCALAR_ARITHMETIC_UNARY(Log10, "log10", "log10_checked") SCALAR_ARITHMETIC_UNARY(Log1p, "log1p", "log1p_checked") SCALAR_ARITHMETIC_UNARY(Log2, "log2", "log2_checked") -SCALAR_ARITHMETIC_UNARY(Sqrt, "sqrt", "sqrt_checked") SCALAR_ARITHMETIC_UNARY(Negate, "negate", "negate_checked") SCALAR_ARITHMETIC_UNARY(Sin, "sin", "sin_checked") +SCALAR_ARITHMETIC_UNARY(Sqrt, "sqrt", "sqrt_checked") SCALAR_ARITHMETIC_UNARY(Tan, "tan", "tan_checked") +SCALAR_EAGER_UNARY(Asinh, "asinh") SCALAR_EAGER_UNARY(Atan, "atan") +SCALAR_EAGER_UNARY(Cosh, "cosh") SCALAR_EAGER_UNARY(Exp, "exp") SCALAR_EAGER_UNARY(Expm1, "expm1") SCALAR_EAGER_UNARY(Sign, "sign") +SCALAR_EAGER_UNARY(Sinh, "sinh") +SCALAR_EAGER_UNARY(Tanh, "tanh") Result Round(const Datum& arg, RoundOptions options, ExecContext* ctx) { return CallFunction("round", {arg}, &options, ctx); diff --git a/cpp/src/arrow/compute/api_scalar.h b/cpp/src/arrow/compute/api_scalar.h index 21daf936fd236..0e5a388b1074f 100644 --- a/cpp/src/arrow/compute/api_scalar.h +++ b/cpp/src/arrow/compute/api_scalar.h @@ -784,6 +784,52 @@ Result Atan(const Datum& arg, ExecContext* ctx = NULLPTR); ARROW_EXPORT Result Atan2(const Datum& y, const Datum& x, ExecContext* ctx = NULLPTR); +/// \brief Compute the hyperbolic sine of the array values. +/// \param[in] arg The values to compute the hyperbolic sine for. +/// \param[in] ctx the function execution context, optional +/// \return the elementwise hyperbolic sine of the values +ARROW_EXPORT +Result Sinh(const Datum& arg, ExecContext* ctx = NULLPTR); + +/// \brief Compute the hyperbolic cosine of the array values. +/// \param[in] arg The values to compute the hyperbolic cosine for. +/// \param[in] ctx the function execution context, optional +/// \return the elementwise hyperbolic cosine of the values +ARROW_EXPORT +Result Cosh(const Datum& arg, ExecContext* ctx = NULLPTR); + +/// \brief Compute the hyperbolic tangent of the array values. +/// \param[in] arg The values to compute the hyperbolic tangent for. +/// \param[in] ctx the function execution context, optional +/// \return the elementwise hyperbolic tangent of the values +ARROW_EXPORT +Result Tanh(const Datum& arg, ExecContext* ctx = NULLPTR); + +/// \brief Compute the inverse hyperbolic sine of the array values. +/// \param[in] arg The values to compute the inverse hyperbolic sine for. +/// \param[in] ctx the function execution context, optional +/// \return the elementwise inverse hyperbolic sine of the values +ARROW_EXPORT +Result Asinh(const Datum& arg, ExecContext* ctx = NULLPTR); + +/// \brief Compute the inverse hyperbolic cosine of the array values. +/// \param[in] arg The values to compute the inverse hyperbolic cosine for. +/// \param[in] options arithmetic options (enable/disable overflow checking), optional +/// \param[in] ctx the function execution context, optional +/// \return the elementwise inverse hyperbolic cosine of the values +ARROW_EXPORT +Result Acosh(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(), + ExecContext* ctx = NULLPTR); + +/// \brief Compute the inverse hyperbolic tangent of the array values. +/// \param[in] arg The values to compute the inverse hyperbolic tangent for. +/// \param[in] options arithmetic options (enable/disable overflow checking), optional +/// \param[in] ctx the function execution context, optional +/// \return the elementwise inverse hyperbolic tangent of the values +ARROW_EXPORT +Result Atanh(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(), + ExecContext* ctx = NULLPTR); + /// \brief Get the natural log of a value. /// /// If argument is null the result will be null. diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc index f11449ad5741f..c13dae573a3d9 100644 --- a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc +++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc @@ -178,6 +178,14 @@ struct SinChecked { } }; +struct Sinh { + template + static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { + static_assert(std::is_same::value, ""); + return std::sinh(val); + } +}; + struct Cos { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { @@ -198,6 +206,14 @@ struct CosChecked { } }; +struct Cosh { + template + static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { + static_assert(std::is_same::value, ""); + return std::cosh(val); + } +}; + struct Tan { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { @@ -219,6 +235,14 @@ struct TanChecked { } }; +struct Tanh { + template + static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { + static_assert(std::is_same::value, ""); + return std::tanh(val); + } +}; + struct Asin { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { @@ -242,6 +266,14 @@ struct AsinChecked { } }; +struct Asinh { + template + static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { + static_assert(std::is_same::value, ""); + return std::asinh(val); + } +}; + struct Acos { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { @@ -265,6 +297,29 @@ struct AcosChecked { } }; +struct Acosh { + template + static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { + static_assert(std::is_same::value, ""); + if (ARROW_PREDICT_FALSE(val < 1.0)) { + return std::numeric_limits::quiet_NaN(); + } + return std::acosh(val); + } +}; + +struct AcoshChecked { + template + static enable_if_floating_value Call(KernelContext*, Arg0 val, Status* st) { + static_assert(std::is_same::value, ""); + if (ARROW_PREDICT_FALSE(val < 1.0)) { + *st = Status::Invalid("domain error"); + return val; + } + return std::acosh(val); + } +}; + struct Atan { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { @@ -273,6 +328,35 @@ struct Atan { } }; +struct Atanh { + template + static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { + static_assert(std::is_same::value, ""); + if (ARROW_PREDICT_FALSE((val < -1.0 || val > 1.0))) { + // N.B. This predicate does *not* match the predicate in AtanhChecked. In + // GH-44630 it was decided that the checked version should error when asked + // for +/- 1 as an input and the unchecked version should return +/- oo + return std::numeric_limits::quiet_NaN(); + } + return std::atanh(val); + } +}; + +struct AtanhChecked { + template + static enable_if_floating_value Call(KernelContext*, Arg0 val, Status* st) { + static_assert(std::is_same::value, ""); + if (ARROW_PREDICT_FALSE((val <= -1.0 || val >= 1.0))) { + // N.B. This predicate does *not* match the predicate in Atanh. In GH-44630 it was + // decided that the checked version should error when asked for +/- 1 as an input + // and the unchecked version should return +/- oo + *st = Status::Invalid("domain error"); + return val; + } + return std::atanh(val); + } +}; + struct Atan2 { template static enable_if_floating_value Call(KernelContext*, Arg0 y, Arg1 x, Status*) { @@ -1178,6 +1262,8 @@ const FunctionDoc sin_checked_doc{"Compute the sine", "to return NaN instead, see \"sin\"."), {"x"}}; +const FunctionDoc sinh_doc{"Compute the hyperbolic sine", (""), {"x"}}; + const FunctionDoc cos_doc{"Compute the cosine", ("NaN is returned for invalid input values;\n" "to raise an error instead, see \"cos_checked\"."), @@ -1188,6 +1274,8 @@ const FunctionDoc cos_checked_doc{"Compute the cosine", "to return NaN instead, see \"cos\"."), {"x"}}; +const FunctionDoc cosh_doc{"Compute the hyperbolic cosine", (""), {"x"}}; + const FunctionDoc tan_doc{"Compute the tangent", ("NaN is returned for invalid input values;\n" "to raise an error instead, see \"tan_checked\"."), @@ -1198,6 +1286,8 @@ const FunctionDoc tan_checked_doc{"Compute the tangent", "to return NaN instead, see \"tan\"."), {"x"}}; +const FunctionDoc tanh_doc{"Compute the hyperbolic tangent", (""), {"x"}}; + const FunctionDoc asin_doc{"Compute the inverse sine", ("NaN is returned for invalid input values;\n" "to raise an error instead, see \"asin_checked\"."), @@ -1208,6 +1298,8 @@ const FunctionDoc asin_checked_doc{"Compute the inverse sine", "to return NaN instead, see \"asin\"."), {"x"}}; +const FunctionDoc asinh_doc{"Compute the inverse hyperbolic sine", (""), {"x"}}; + const FunctionDoc acos_doc{"Compute the inverse cosine", ("NaN is returned for invalid input values;\n" "to raise an error instead, see \"acos_checked\"."), @@ -1218,6 +1310,16 @@ const FunctionDoc acos_checked_doc{"Compute the inverse cosine", "to return NaN instead, see \"acos\"."), {"x"}}; +const FunctionDoc acosh_doc{"Compute the inverse hyperbolic cosine", + ("NaN is returned for input values < 1.0;\n" + "to raise an error instead, see \"acosh_checked\"."), + {"x"}}; + +const FunctionDoc acosh_checked_doc{"Compute the inverse hyperbolic cosine", + ("Input values < 1.0 raise an error;\n" + "to return NaN instead, see \"acosh\"."), + {"x"}}; + const FunctionDoc atan_doc{"Compute the inverse tangent of x", ("The return value is in the range [-pi/2, pi/2];\n" "for a full return range [-pi, pi], see \"atan2\"."), @@ -1227,6 +1329,17 @@ const FunctionDoc atan2_doc{"Compute the inverse tangent of y/x", ("The return value is in the range [-pi, pi]."), {"y", "x"}}; +const FunctionDoc atanh_doc{"Compute the inverse hyperbolic tangent", + ("NaN is returned for input values x with |x| > 1.\n" + "At x = +/- 1, returns +/- infinity.\n" + "To raise an error instead, see \"atanh_checked\"."), + {"x"}}; + +const FunctionDoc atanh_checked_doc{"Compute the inverse hyperbolic tangent", + ("Input values x with |x| >= 1.0 raise an error\n" + "to return NaN instead, see \"atanh\"."), + {"x"}}; + const FunctionDoc ln_doc{ "Compute natural logarithm", ("Non-positive values return -inf or NaN. Null values return null.\n" @@ -1691,6 +1804,9 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) { "sin_checked", sin_checked_doc); DCHECK_OK(registry->AddFunction(std::move(sin_checked))); + auto sinh = MakeUnaryArithmeticFunctionFloatingPoint("sinh", sinh_doc); + DCHECK_OK(registry->AddFunction(std::move(sinh))); + auto cos = MakeUnaryArithmeticFunctionFloatingPoint("cos", cos_doc); DCHECK_OK(registry->AddFunction(std::move(cos))); @@ -1698,6 +1814,9 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) { "cos_checked", cos_checked_doc); DCHECK_OK(registry->AddFunction(std::move(cos_checked))); + auto cosh = MakeUnaryArithmeticFunctionFloatingPoint("cosh", cosh_doc); + DCHECK_OK(registry->AddFunction(std::move(cosh))); + auto tan = MakeUnaryArithmeticFunctionFloatingPoint("tan", tan_doc); DCHECK_OK(registry->AddFunction(std::move(tan))); @@ -1705,6 +1824,9 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) { "tan_checked", tan_checked_doc); DCHECK_OK(registry->AddFunction(std::move(tan_checked))); + auto tanh = MakeUnaryArithmeticFunctionFloatingPoint("tanh", tanh_doc); + DCHECK_OK(registry->AddFunction(std::move(tanh))); + auto asin = MakeUnaryArithmeticFunctionFloatingPoint("asin", asin_doc); DCHECK_OK(registry->AddFunction(std::move(asin))); @@ -1712,6 +1834,9 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) { "asin_checked", asin_checked_doc); DCHECK_OK(registry->AddFunction(std::move(asin_checked))); + auto asinh = MakeUnaryArithmeticFunctionFloatingPoint("asinh", asinh_doc); + DCHECK_OK(registry->AddFunction(std::move(asinh))); + auto acos = MakeUnaryArithmeticFunctionFloatingPoint("acos", acos_doc); DCHECK_OK(registry->AddFunction(std::move(acos))); @@ -1719,12 +1844,26 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) { "acos_checked", acos_checked_doc); DCHECK_OK(registry->AddFunction(std::move(acos_checked))); + auto acosh = MakeUnaryArithmeticFunctionFloatingPoint("acosh", acosh_doc); + DCHECK_OK(registry->AddFunction(std::move(acosh))); + + auto acosh_checked = MakeUnaryArithmeticFunctionFloatingPointNotNull( + "acosh_checked", acosh_checked_doc); + DCHECK_OK(registry->AddFunction(std::move(acosh_checked))); + auto atan = MakeUnaryArithmeticFunctionFloatingPoint("atan", atan_doc); DCHECK_OK(registry->AddFunction(std::move(atan))); auto atan2 = MakeArithmeticFunctionFloatingPoint("atan2", atan2_doc); DCHECK_OK(registry->AddFunction(std::move(atan2))); + auto atanh = MakeUnaryArithmeticFunctionFloatingPoint("atanh", atanh_doc); + DCHECK_OK(registry->AddFunction(std::move(atanh))); + + auto atanh_checked = MakeUnaryArithmeticFunctionFloatingPointNotNull( + "atanh_checked", atanh_checked_doc); + DCHECK_OK(registry->AddFunction(std::move(atanh_checked))); + // ---------------------------------------------------------------------- // Logarithms auto ln = MakeUnaryArithmeticFunctionFloatingPoint("ln", ln_doc); diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc b/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc index 9cabebc3f4e46..9a1a569081d9a 100644 --- a/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc +++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc @@ -15,6 +15,9 @@ // specific language governing permissions and limitations // under the License. +// Required for Windows to define M_LN* constants +#define _USE_MATH_DEFINES + #include #include #include @@ -1186,8 +1189,8 @@ TEST(TestUnaryArithmetic, DispatchBest) { } // Float types (with _checked variant) - for (std::string name : - {"ln", "log2", "log10", "log1p", "sin", "cos", "tan", "asin", "acos"}) { + for (std::string name : {"ln", "log2", "log10", "log1p", "sin", "cos", "tan", "asin", + "acos", "acosh", "atanh"}) { for (std::string suffix : {"", "_checked"}) { name += suffix; for (const auto& ty : {float32(), float64()}) { @@ -1198,7 +1201,7 @@ TEST(TestUnaryArithmetic, DispatchBest) { } // Float types - for (std::string name : {"atan", "sign", "exp"}) { + for (std::string name : {"sinh", "cosh", "tanh", "asinh", "atan", "sign", "exp"}) { for (const auto& ty : {float32(), float64()}) { CheckDispatchBest(name, {ty}, {ty}); CheckDispatchBest(name, {dictionary(int8(), ty)}, {ty}); @@ -1206,8 +1209,8 @@ TEST(TestUnaryArithmetic, DispatchBest) { } // Integer -> Float64 (with _checked variant) - for (std::string name : - {"ln", "log2", "log10", "log1p", "sin", "cos", "tan", "asin", "acos"}) { + for (std::string name : {"ln", "log2", "log10", "log1p", "sin", "cos", "tan", "asin", + "acos", "acosh", "atanh"}) { for (std::string suffix : {"", "_checked"}) { name += suffix; for (const auto& ty : @@ -1219,7 +1222,7 @@ TEST(TestUnaryArithmetic, DispatchBest) { } // Integer -> Float64 - for (std::string name : {"atan"}) { + for (std::string name : {"sinh", "cosh", "tanh", "asinh", "atan"}) { for (const auto& ty : {int8(), int16(), int32(), int64(), uint8(), uint16(), uint32(), uint64()}) { CheckDispatchBest(name, {ty}, {float64()}); @@ -1229,15 +1232,16 @@ TEST(TestUnaryArithmetic, DispatchBest) { } TEST(TestUnaryArithmetic, Null) { - for (std::string name : {"abs", "acos", "asin", "cos", "ln", "log10", "log1p", "log2", - "negate", "sin", "tan"}) { + for (std::string name : {"abs", "acos", "acosh", "asin", "atanh", "cos", "ln", "log10", + "log1p", "log2", "negate", "sin", "tan"}) { for (std::string suffix : {"", "_checked"}) { name += suffix; AssertNullToNull(name); } } - for (std::string name : {"atan", "bit_wise_not", "sign"}) { + for (std::string name : + {"sinh", "cosh", "tanh", "asinh", "atan", "bit_wise_not", "sign"}) { AssertNullToNull(name); } } @@ -2497,6 +2501,18 @@ TYPED_TEST(TestUnaryArithmeticFloating, TrigSin) { this->AssertUnaryOpRaises(Sin, "[Inf, -Inf]", "domain error"); } +TYPED_TEST(TestUnaryArithmeticFloating, TrigSinh) { + this->SetNansEqual(true); + auto sinh = [](const Datum& arg, ArithmeticOptions, ExecContext* ctx) { + return Sinh(arg, ctx); + }; + + this->AssertUnaryOp(sinh, "[Inf, -Inf]", "[Inf, -Inf]"); + this->AssertUnaryOp(sinh, "[]", "[]"); + this->AssertUnaryOp(sinh, "[null, NaN]", "[null, NaN]"); + this->AssertUnaryOp(sinh, MakeArray(0, M_LN2, M_LN10), "[0, 0.75, 4.95]"); +} + TYPED_TEST(TestUnaryArithmeticFloating, TrigCos) { this->SetNansEqual(true); this->AssertUnaryOp(Cos, "[Inf, -Inf]", "[NaN, NaN]"); @@ -2509,6 +2525,18 @@ TYPED_TEST(TestUnaryArithmeticFloating, TrigCos) { this->AssertUnaryOpRaises(Cos, "[Inf, -Inf]", "domain error"); } +TYPED_TEST(TestUnaryArithmeticFloating, TrigCosh) { + this->SetNansEqual(true); + auto cosh = [](const Datum& arg, ArithmeticOptions, ExecContext* ctx) { + return Cosh(arg, ctx); + }; + + this->AssertUnaryOp(cosh, "[Inf, -Inf]", "[Inf, Inf]"); + this->AssertUnaryOp(cosh, "[]", "[]"); + this->AssertUnaryOp(cosh, "[null, NaN]", "[null, NaN]"); + this->AssertUnaryOp(cosh, MakeArray(0, M_LN2, M_LN10), "[1, 1.25, 5.05]"); +} + TYPED_TEST(TestUnaryArithmeticFloating, TrigTan) { this->SetNansEqual(true); this->AssertUnaryOp(Tan, "[Inf, -Inf]", "[NaN, NaN]"); @@ -2523,6 +2551,18 @@ TYPED_TEST(TestUnaryArithmeticFloating, TrigTan) { this->AssertUnaryOpRaises(Tan, "[Inf, -Inf]", "domain error"); } +TYPED_TEST(TestUnaryArithmeticFloating, TrigTanh) { + this->SetNansEqual(true); + auto tanh = [](const Datum& arg, ArithmeticOptions, ExecContext* ctx) { + return Tanh(arg, ctx); + }; + + this->AssertUnaryOp(tanh, "[Inf, -Inf]", "[1, -1]"); + this->AssertUnaryOp(tanh, "[]", "[]"); + this->AssertUnaryOp(tanh, "[null, NaN]", "[null, NaN]"); + this->AssertUnaryOp(tanh, MakeArray(0, M_LN2), "[0, 0.6]"); +} + TYPED_TEST(TestUnaryArithmeticFloating, TrigAsin) { this->SetNansEqual(true); this->AssertUnaryOp(Asin, "[Inf, -Inf, -2, 2]", "[NaN, NaN, NaN, NaN]"); @@ -2535,6 +2575,18 @@ TYPED_TEST(TestUnaryArithmeticFloating, TrigAsin) { this->AssertUnaryOpRaises(Asin, "[Inf, -Inf, -2, 2]", "domain error"); } +TYPED_TEST(TestUnaryArithmeticFloating, TrigAsinh) { + this->SetNansEqual(true); + auto asinh = [](const Datum& arg, ArithmeticOptions, ExecContext* ctx) { + return Asinh(arg, ctx); + }; + + this->AssertUnaryOp(asinh, "[Inf, -Inf]", "[Inf, -Inf]"); + this->AssertUnaryOp(asinh, "[]", "[]"); + this->AssertUnaryOp(asinh, "[null, NaN]", "[null, NaN]"); + this->AssertUnaryOp(asinh, "[0, 0.75, 4.95]", MakeArray(0, M_LN2, M_LN10)); +} + TYPED_TEST(TestUnaryArithmeticFloating, TrigAcos) { this->SetNansEqual(true); this->AssertUnaryOp(Asin, "[Inf, -Inf, -2, 2]", "[NaN, NaN, NaN, NaN]"); @@ -2547,6 +2599,18 @@ TYPED_TEST(TestUnaryArithmeticFloating, TrigAcos) { this->AssertUnaryOpRaises(Acos, "[Inf, -Inf, -2, 2]", "domain error"); } +TYPED_TEST(TestUnaryArithmeticFloating, TrigAcosh) { + this->SetNansEqual(true); + this->AssertUnaryOp(Acosh, "[0, -1, -Inf]", "[NaN, NaN, NaN]"); + for (auto check_overflow : {false, true}) { + this->SetOverflowCheck(check_overflow); + this->AssertUnaryOp(Acosh, "[]", "[]"); + this->AssertUnaryOp(Acosh, "[null, NaN]", "[null, NaN]"); + this->AssertUnaryOp(Acosh, "[1, 1.25, 5.05]", MakeArray(0, M_LN2, M_LN10)); + } + this->AssertUnaryOpRaises(Acosh, "[0, -1, -Inf]", "domain error"); +} + TYPED_TEST(TestUnaryArithmeticFloating, TrigAtan) { this->SetNansEqual(true); auto atan = [](const Datum& arg, ArithmeticOptions, ExecContext* ctx) { @@ -2572,6 +2636,19 @@ TYPED_TEST(TestBinaryArithmeticFloating, TrigAtan2) { -M_PI_2, 0, M_PI)); } +TYPED_TEST(TestUnaryArithmeticFloating, TrigAtanh) { + this->SetNansEqual(true); + this->AssertUnaryOp(Atanh, "[-Inf, Inf, -2, 2]", "[NaN, NaN, NaN, NaN]"); + this->AssertUnaryOp(Atanh, "[-1, 1]", "[-Inf, Inf]"); + for (auto check_overflow : {false, true}) { + this->SetOverflowCheck(check_overflow); + this->AssertUnaryOp(Atanh, "[]", "[]"); + this->AssertUnaryOp(Atanh, "[null, NaN]", "[null, NaN]"); + this->AssertUnaryOp(Atanh, "[0, 0.6]", MakeArray(0, M_LN2)); + } + this->AssertUnaryOpRaises(Atanh, "[-Inf, Inf, -1, 1, -2, 2]", "domain error"); +} + TYPED_TEST(TestUnaryArithmeticIntegral, Trig) { // Integer arguments promoted to double, sanity check here auto atan = [](const Datum& arg, ArithmeticOptions, ExecContext* ctx) { diff --git a/cpp/src/arrow/engine/substrait/extension_set.cc b/cpp/src/arrow/engine/substrait/extension_set.cc index cefe53d2847ca..ac25eba684bca 100644 --- a/cpp/src/arrow/engine/substrait/extension_set.cc +++ b/cpp/src/arrow/engine/substrait/extension_set.cc @@ -1071,7 +1071,8 @@ struct DefaultExtensionIdRegistry : ExtensionIdRegistryImpl { // Mappings either without a _checked variant or substrait has no overflow option for (const auto& function_name : - {"exp", "sign", "cos", "sin", "tan", "acos", "asin", "atan", "atan2"}) { + {"exp", "sign", "cos", "cosh", "sin", "sinh", "tan", "tanh", "acos", "acosh", + "asin", "asinh", "atan", "atanh", "atan2"}) { DCHECK_OK( AddSubstraitCallToArrow({kSubstraitArithmeticFunctionsUri, function_name}, DecodeOptionlessUncheckedArithmetic(function_name))); @@ -1207,7 +1208,13 @@ struct DefaultExtensionIdRegistry : ExtensionIdRegistryImpl { {kSubstraitArithmeticFunctionsUri, "acos"}, {kSubstraitArithmeticFunctionsUri, "asin"}, {kSubstraitArithmeticFunctionsUri, "atan"}, - {kSubstraitArithmeticFunctionsUri, "atan2"}}) { + {kSubstraitArithmeticFunctionsUri, "atan2"}, + {kSubstraitArithmeticFunctionsUri, "cosh"}, + {kSubstraitArithmeticFunctionsUri, "sinh"}, + {kSubstraitArithmeticFunctionsUri, "tanh"}, + {kSubstraitArithmeticFunctionsUri, "acosh"}, + {kSubstraitArithmeticFunctionsUri, "asinh"}, + {kSubstraitArithmeticFunctionsUri, "atanh"}}) { Id fn_id{fn_pair.first, fn_pair.second}; DCHECK_OK(AddArrowToSubstraitCall(std::string(fn_pair.second), EncodeBasic(fn_id))); } diff --git a/docs/source/cpp/compute.rst b/docs/source/cpp/compute.rst index 3c264fb4767a1..ec53fb04688bc 100644 --- a/docs/source/cpp/compute.rst +++ b/docs/source/cpp/compute.rst @@ -721,6 +721,35 @@ Decimal values are accepted, but are cast to Float64 first. | tan_checked | Unary | Float32/Float64/Decimal | Float32/Float64 | +--------------------------+------------+-------------------------+---------------------+ +Hyperbolic trigonometric functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Hyperbolic trigonometric functions are also supported, and, where applicable, also offer +``_checked`` variants that check for domain errors if needed. + +Decimal values are accepted, but are cast to Float64 first. + ++--------------------------+------------+-------------------------+---------------------+ +| Function name | Arity | Input types | Output type | ++==========================+============+=========================+=====================+ +| acosh | Unary | Float32/Float64/Decimal | Float32/Float64 | ++--------------------------+------------+-------------------------+---------------------+ +| acosh_checked | Unary | Float32/Float64/Decimal | Float32/Float64 | ++--------------------------+------------+-------------------------+---------------------+ +| asinh | Unary | Float32/Float64/Decimal | Float32/Float64 | ++--------------------------+------------+-------------------------+---------------------+ +| atanh | Unary | Float32/Float64/Decimal | Float32/Float64 | ++--------------------------+------------+-------------------------+---------------------+ +| atanh_checked | Unary | Float32/Float64/Decimal | Float32/Float64 | ++--------------------------+------------+-------------------------+---------------------+ +| cosh | Unary | Float32/Float64/Decimal | Float32/Float64 | ++--------------------------+------------+-------------------------+---------------------+ +| sinh | Unary | Float32/Float64/Decimal | Float32/Float64 | ++--------------------------+------------+-------------------------+---------------------+ +| tanh | Unary | Float32/Float64/Decimal | Float32/Float64 | ++--------------------------+------------+-------------------------+---------------------+ + + Comparisons ~~~~~~~~~~~ diff --git a/python/pyarrow/tests/test_compute.py b/python/pyarrow/tests/test_compute.py index c16d2f9aacf74..e388851bea17b 100644 --- a/python/pyarrow/tests/test_compute.py +++ b/python/pyarrow/tests/test_compute.py @@ -3369,9 +3369,10 @@ def create_sample_expressions(): g = pc.scalar(pa.scalar(1)) h = pc.scalar(np.int64(2)) j = pc.scalar(False) + k = pc.scalar(0) # These expression consist entirely of literals - literal_exprs = [a, b, c, d, e, g, h, j] + literal_exprs = [a, b, c, d, e, g, h, j, k] # These expressions include at least one function call exprs_with_call = [a == b, a != b, a > b, c & j, c | j, ~c, d.is_valid(), @@ -3380,6 +3381,8 @@ def create_sample_expressions(): pc.multiply(a, b), pc.power(a, a), pc.sqrt(a), pc.exp(b), pc.cos(b), pc.sin(b), pc.tan(b), pc.acos(b), pc.atan(b), pc.asin(b), pc.atan2(b, b), + pc.sinh(a), pc.cosh(a), pc.tanh(a), + pc.asinh(a), pc.acosh(b), pc.atanh(k), pc.abs(b), pc.sign(a), pc.bit_wise_not(a), pc.bit_wise_and(a, a), pc.bit_wise_or(a, a), pc.bit_wise_xor(a, a), pc.is_nan(b), pc.is_finite(b),