This is an automated email from the ASF dual-hosted git repository.
felipecrv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new 104b0406ab GH-44952: [C++][Python] Add Hyperbolic Trig functions
(#44630)
104b0406ab is described below
commit 104b0406ab7e6bfc296f64649a6a71314436f06e
Author: Kevin Wilson <[email protected]>
AuthorDate: Mon Dec 9 17:40:09 2024 -0500
GH-44952: [C++][Python] Add Hyperbolic Trig functions (#44630)
### Rationale for this change
Hyperbolic trigonometric functions are a common transformation for dealing
with skewed data. And they are built into the core C++ libraries so require
minimal change.
### What changes are included in this PR?
Adding `a?(sin|cos|tan)h` to the base C++ library and substrait and tests
for these functions in pyarrow.
### Are these changes tested?
Yes, in the same style as the trigonometric functions.
### Are there any user-facing changes?
Yes. Additional compute functions are added.
* GitHub Issue: #44952
Authored-by: Kevin H Wilson <[email protected]>
Signed-off-by: Felipe Oliveira Carvalho <[email protected]>
---
cpp/src/arrow/compute/api_scalar.cc | 8 +-
cpp/src/arrow/compute/api_scalar.h | 46 +++++++
cpp/src/arrow/compute/kernels/scalar_arithmetic.cc | 139 +++++++++++++++++++++
.../compute/kernels/scalar_arithmetic_test.cc | 95 ++++++++++++--
cpp/src/arrow/engine/substrait/extension_set.cc | 11 +-
docs/source/cpp/compute.rst | 29 +++++
python/pyarrow/tests/test_compute.py | 5 +-
7 files changed, 320 insertions(+), 13 deletions(-)
diff --git a/cpp/src/arrow/compute/api_scalar.cc
b/cpp/src/arrow/compute/api_scalar.cc
index f00fad29d7..61a16f5f5e 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<Datum> 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 21daf936fd..0e5a388b10 100644
--- a/cpp/src/arrow/compute/api_scalar.h
+++ b/cpp/src/arrow/compute/api_scalar.h
@@ -784,6 +784,52 @@ Result<Datum> Atan(const Datum& arg, ExecContext* ctx =
NULLPTR);
ARROW_EXPORT
Result<Datum> 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<Datum> 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<Datum> 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<Datum> 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<Datum> 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<Datum> 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<Datum> 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 f11449ad57..c13dae573a 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 <typename T, typename Arg0>
+ static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
+ static_assert(std::is_same<T, Arg0>::value, "");
+ return std::sinh(val);
+ }
+};
+
struct Cos {
template <typename T, typename Arg0>
static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
@@ -198,6 +206,14 @@ struct CosChecked {
}
};
+struct Cosh {
+ template <typename T, typename Arg0>
+ static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
+ static_assert(std::is_same<T, Arg0>::value, "");
+ return std::cosh(val);
+ }
+};
+
struct Tan {
template <typename T, typename Arg0>
static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
@@ -219,6 +235,14 @@ struct TanChecked {
}
};
+struct Tanh {
+ template <typename T, typename Arg0>
+ static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
+ static_assert(std::is_same<T, Arg0>::value, "");
+ return std::tanh(val);
+ }
+};
+
struct Asin {
template <typename T, typename Arg0>
static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
@@ -242,6 +266,14 @@ struct AsinChecked {
}
};
+struct Asinh {
+ template <typename T, typename Arg0>
+ static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
+ static_assert(std::is_same<T, Arg0>::value, "");
+ return std::asinh(val);
+ }
+};
+
struct Acos {
template <typename T, typename Arg0>
static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
@@ -265,6 +297,29 @@ struct AcosChecked {
}
};
+struct Acosh {
+ template <typename T, typename Arg0>
+ static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
+ static_assert(std::is_same<T, Arg0>::value, "");
+ if (ARROW_PREDICT_FALSE(val < 1.0)) {
+ return std::numeric_limits<T>::quiet_NaN();
+ }
+ return std::acosh(val);
+ }
+};
+
+struct AcoshChecked {
+ template <typename T, typename Arg0>
+ static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status* st) {
+ static_assert(std::is_same<T, Arg0>::value, "");
+ if (ARROW_PREDICT_FALSE(val < 1.0)) {
+ *st = Status::Invalid("domain error");
+ return val;
+ }
+ return std::acosh(val);
+ }
+};
+
struct Atan {
template <typename T, typename Arg0>
static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
@@ -273,6 +328,35 @@ struct Atan {
}
};
+struct Atanh {
+ template <typename T, typename Arg0>
+ static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status*) {
+ static_assert(std::is_same<T, Arg0>::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<T>::quiet_NaN();
+ }
+ return std::atanh(val);
+ }
+};
+
+struct AtanhChecked {
+ template <typename T, typename Arg0>
+ static enable_if_floating_value<Arg0, T> Call(KernelContext*, Arg0 val,
Status* st) {
+ static_assert(std::is_same<T, Arg0>::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 <typename T, typename Arg0, typename Arg1>
static enable_if_floating_value<Arg0, T> 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", sinh_doc);
+ DCHECK_OK(registry->AddFunction(std::move(sinh)));
+
auto cos = MakeUnaryArithmeticFunctionFloatingPoint<Cos>("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", cosh_doc);
+ DCHECK_OK(registry->AddFunction(std::move(cosh)));
+
auto tan = MakeUnaryArithmeticFunctionFloatingPoint<Tan>("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", tanh_doc);
+ DCHECK_OK(registry->AddFunction(std::move(tanh)));
+
auto asin = MakeUnaryArithmeticFunctionFloatingPoint<Asin>("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",
asinh_doc);
+ DCHECK_OK(registry->AddFunction(std::move(asinh)));
+
auto acos = MakeUnaryArithmeticFunctionFloatingPoint<Acos>("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",
acosh_doc);
+ DCHECK_OK(registry->AddFunction(std::move(acosh)));
+
+ auto acosh_checked =
MakeUnaryArithmeticFunctionFloatingPointNotNull<AcoshChecked>(
+ "acosh_checked", acosh_checked_doc);
+ DCHECK_OK(registry->AddFunction(std::move(acosh_checked)));
+
auto atan = MakeUnaryArithmeticFunctionFloatingPoint<Atan>("atan", atan_doc);
DCHECK_OK(registry->AddFunction(std::move(atan)));
auto atan2 = MakeArithmeticFunctionFloatingPoint<Atan2>("atan2", atan2_doc);
DCHECK_OK(registry->AddFunction(std::move(atan2)));
+ auto atanh = MakeUnaryArithmeticFunctionFloatingPoint<Atanh>("atanh",
atanh_doc);
+ DCHECK_OK(registry->AddFunction(std::move(atanh)));
+
+ auto atanh_checked =
MakeUnaryArithmeticFunctionFloatingPointNotNull<AtanhChecked>(
+ "atanh_checked", atanh_checked_doc);
+ DCHECK_OK(registry->AddFunction(std::move(atanh_checked)));
+
// ----------------------------------------------------------------------
// Logarithms
auto ln = MakeUnaryArithmeticFunctionFloatingPoint<LogNatural>("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 9cabebc3f4..9a1a569081 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 <algorithm>
#include <cmath>
#include <memory>
@@ -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 cefe53d284..ac25eba684 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 3c264fb476..ec53fb0468 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 c16d2f9aac..e388851bea 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),