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),

Reply via email to