From 0dc72a566d5feb0d9748943c58ba1c6a4c495861 Mon Sep 17 00:00:00 2001 From: samukweku Date: Mon, 3 Apr 2023 22:44:49 +1000 Subject: [PATCH] trigonometric fexpr --- docs/api/math/arccos.rst | 2 +- docs/api/math/arcsin.rst | 2 +- docs/api/math/arctan.rst | 2 +- docs/api/math/cos.rst | 2 +- docs/api/math/deg2rad.rst | 2 +- docs/api/math/rad2deg.rst | 2 +- docs/api/math/sin.rst | 2 +- docs/api/math/tan.rst | 2 +- src/core/expr/funary/trigonometric.cc | 390 +++++++++++++++----------- 9 files changed, 242 insertions(+), 164 deletions(-) diff --git a/docs/api/math/arccos.rst b/docs/api/math/arccos.rst index 40f7299d8d..ad7282f71e 100644 --- a/docs/api/math/arccos.rst +++ b/docs/api/math/arccos.rst @@ -1,6 +1,6 @@ .. xfunction:: datatable.math.arccos - :src: src/core/expr/funary/trigonometric.cc resolve_op_arccos + :src: src/core/expr/funary/trigonometric.cc pyfn_arccos :cvar: doc_math_arccos :signature: arccos(x) diff --git a/docs/api/math/arcsin.rst b/docs/api/math/arcsin.rst index 9ef886ce41..d0a60b1ec6 100644 --- a/docs/api/math/arcsin.rst +++ b/docs/api/math/arcsin.rst @@ -1,6 +1,6 @@ .. xfunction:: datatable.math.arcsin - :src: src/core/expr/funary/trigonometric.cc resolve_op_arcsin + :src: src/core/expr/funary/trigonometric.cc pyfn_arcsin :cvar: doc_math_arcsin :signature: arcsin(x) diff --git a/docs/api/math/arctan.rst b/docs/api/math/arctan.rst index d543189565..ee1e2bd388 100644 --- a/docs/api/math/arctan.rst +++ b/docs/api/math/arctan.rst @@ -1,6 +1,6 @@ .. xfunction:: datatable.math.arctan - :src: src/core/expr/funary/trigonometric.cc resolve_op_arctan + :src: src/core/expr/funary/trigonometric.cc pyfn_arctan :cvar: doc_math_arctan :signature: arctan(x) diff --git a/docs/api/math/cos.rst b/docs/api/math/cos.rst index 46ed8d7fd6..73f1913b0a 100644 --- a/docs/api/math/cos.rst +++ b/docs/api/math/cos.rst @@ -1,6 +1,6 @@ .. xfunction:: datatable.math.cos - :src: src/core/expr/funary/trigonometric.cc resolve_op_cos + :src: src/core/expr/funary/trigonometric.cc pyfn_cos :cvar: doc_math_cos :signature: cos(x) diff --git a/docs/api/math/deg2rad.rst b/docs/api/math/deg2rad.rst index 030548dc3e..82912e5ccd 100644 --- a/docs/api/math/deg2rad.rst +++ b/docs/api/math/deg2rad.rst @@ -1,6 +1,6 @@ .. xfunction:: datatable.math.deg2rad - :src: src/core/expr/funary/trigonometric.cc resolve_op_deg2rad + :src: src/core/expr/funary/trigonometric.cc pyfn_deg2rad :cvar: doc_math_deg2rad :signature: deg2rad(x) diff --git a/docs/api/math/rad2deg.rst b/docs/api/math/rad2deg.rst index 8d0f650ff4..fe8c9853ad 100644 --- a/docs/api/math/rad2deg.rst +++ b/docs/api/math/rad2deg.rst @@ -1,6 +1,6 @@ .. xfunction:: datatable.math.rad2deg - :src: src/core/expr/funary/trigonometric.cc resolve_op_rad2deg + :src: src/core/expr/funary/trigonometric.cc pyfn_rad2deg :cvar: doc_math_rad2deg :signature: rad2deg(x) diff --git a/docs/api/math/sin.rst b/docs/api/math/sin.rst index 1a1328b9c6..20f491ad0f 100644 --- a/docs/api/math/sin.rst +++ b/docs/api/math/sin.rst @@ -1,6 +1,6 @@ .. xfunction:: datatable.math.sin - :src: src/core/expr/funary/trigonometric.cc resolve_op_sin + :src: src/core/expr/funary/trigonometric.cc pyfn_sin :cvar: doc_math_sin :signature: sin(x) diff --git a/docs/api/math/tan.rst b/docs/api/math/tan.rst index ffec7d1a89..6632589f03 100644 --- a/docs/api/math/tan.rst +++ b/docs/api/math/tan.rst @@ -1,6 +1,6 @@ .. xfunction:: datatable.math.tan - :src: src/core/expr/funary/trigonometric.cc resolve_op_tan + :src: src/core/expr/funary/trigonometric.cc pyfn_tan :cvar: doc_math_tan :signature: tan(x) diff --git a/src/core/expr/funary/trigonometric.cc b/src/core/expr/funary/trigonometric.cc index 10169b0f27..6fc8a30373 100644 --- a/src/core/expr/funary/trigonometric.cc +++ b/src/core/expr/funary/trigonometric.cc @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Copyright 2019-2021 H2O.ai +// Copyright 2023 H2O.ai // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -20,179 +20,257 @@ // IN THE SOFTWARE. //------------------------------------------------------------------------------ #include +#include "column/const.h" +#include "column/func_unary.h" #include "documentation.h" -#include "expr/funary/pyfn.h" -#include "expr/funary/umaker.h" -#include "expr/funary/umaker_impl.h" -#include "ltype.h" +#include "expr/fexpr_func_unary.h" +#include "python/xargs.h" +#include "stype.h" namespace dt { namespace expr { - -using func32_t = float(*)(float); -using func64_t = double(*)(double); - -/** - * All standard trigonometric functions have the same signature: - * - * VOID -> VOID - * {BOOL, INT*, FLOAT64} -> FLOAT64 - * FLOAT32 -> FLOAT32 - * - */ -static umaker_ptr _resolve_trig(SType stype, const char* name, - func32_t fn32, func64_t fn64) -{ - if (stype == SType::VOID) { - return umaker_ptr(new umaker_copy()); - } - if (stype == SType::FLOAT64) { - return umaker1::make(fn64, SType::AUTO, SType::FLOAT64); - } - if (stype == SType::FLOAT32) { - return umaker1::make(fn32, SType::AUTO, SType::FLOAT32); - } - if (stype == SType::BOOL || stype_to_ltype(stype) == LType::INT) { - return umaker1::make(fn64, SType::FLOAT64, SType::FLOAT64); - } - throw TypeError() << "Function `" << name << "` cannot be applied to a " - "column of type `" << stype << "`"; -} - - - - -//------------------------------------------------------------------------------ -// Op::SIN -//------------------------------------------------------------------------------ - -py::PKArgs args_sin(1, 0, 0, false, false, {"x"}, "sin", dt::doc_math_sin); - - -umaker_ptr resolve_op_sin(SType stype) { - return _resolve_trig(stype, "sin", &std::sin, &std::sin); -} - - - - -//------------------------------------------------------------------------------ -// Op::COS -//------------------------------------------------------------------------------ - -py::PKArgs args_cos(1, 0, 0, false, false, {"x"}, "cos", dt::doc_math_cos); - - -umaker_ptr resolve_op_cos(SType stype) { - return _resolve_trig(stype, "cos", &std::cos, &std::cos); -} - - - - -//------------------------------------------------------------------------------ -// Op::TAN -//------------------------------------------------------------------------------ - -py::PKArgs args_tan(1, 0, 0, false, false, {"x"}, "tan", dt::doc_math_tan); - - -umaker_ptr resolve_op_tan(SType stype) { - return _resolve_trig(stype, "tan", &std::tan, &std::tan); -} - - - - -//------------------------------------------------------------------------------ -// Op::ARCSIN -//------------------------------------------------------------------------------ - -py::PKArgs args_arcsin(1, 0, 0, false, false, {"x"}, "arcsin", dt::doc_math_arcsin); - - -umaker_ptr resolve_op_arcsin(SType stype) { - return _resolve_trig(stype, "arcsin", &std::asin, &std::asin); +template +class FExpr_Trigonometric : public FExpr_FuncUnary { + public: + using FExpr_FuncUnary::FExpr_FuncUnary; + + static std::string function_name() { + switch (POS) { + case 1: + return "sin"; + case 2: + return "cos"; + case 3: + return "tan"; + case 4: + return "arcsin"; + case 5: + return "arccos"; + case 6: + return "arctan"; + case 7: + return "deg2rad"; + case 8: + return "rag2deg"; + } + + } + + + std::string name() const override { + return function_name(); + } + + /** + * All standard hyperbolic functions have the same signature: + * + * VOID -> VOID + * {BOOL, INT*, FLOAT64} -> FLOAT64 + * FLOAT32 -> FLOAT32 + * + */ + template + static inline T op_trigonometric(T x) { + switch (POSN) { + case 1: + return sin(x); + case 2: + return cos(x); + case 3: + return tan(x); + case 4: + return asin(x); + case 5: + return acos(x); + case 6: + return atan(x); + } + } + + template + static inline T f64_deg2rad(T x) { + static constexpr T RADIANS_IN_DEGREE = 0.017453292519943295; + return x * RADIANS_IN_DEGREE; + } + + template + static T f32_deg2rad(T x) { + static constexpr T RADIANS_IN_DEGREE = 0.0174532924f; + return x * RADIANS_IN_DEGREE; + } + + template + static T f64_rad2deg(T x) { + static constexpr T DEGREES_IN_RADIAN = 57.295779513082323; + return x * DEGREES_IN_RADIAN; + } + + template + static T f32_rad2deg(T x) { + static constexpr T DEGREES_IN_RADIAN = 57.2957802f; + return x * DEGREES_IN_RADIAN; + } + + + Column evaluate1(Column&& col) const override{ + SType stype = col.stype(); + + switch (stype) { + case SType::VOID: + return Column(new ConstNa_ColumnImpl(col.nrows(), SType::VOID)); + case SType::BOOL: + case SType::INT8: + case SType::INT16: + case SType::INT32: + case SType::INT64: + col.cast_inplace(SType::FLOAT64); + return make(std::move(col)); + case SType::FLOAT32: + return make(std::move(col)); + case SType::FLOAT64: + return make(std::move(col)); + default: + std::string func_name = function_name(); + throw TypeError() << "Function `" << func_name << "` cannot be applied to a " + "column of type `" << stype << "`"; + } + } + + private: + template + static Column make(Column&& col) { + xassert(compatible_type(col.stype())); + if (POSS < 7){ + return Column(new FuncUnary1_ColumnImpl( + std::move(col), op_trigonometric, + col.nrows(), col.stype() + )); + } + if (POSS == 7) { + if (col.stype() == SType::FLOAT64) { + return Column(new FuncUnary1_ColumnImpl( + std::move(col), f64_deg2rad, + col.nrows(), col.stype() + )); + } + return Column(new FuncUnary1_ColumnImpl( + std::move(col), f32_deg2rad, + col.nrows(), col.stype() + )); + + } + if (POSS == 8) { + if (col.stype() == SType::FLOAT64) { + return Column(new FuncUnary1_ColumnImpl( + std::move(col), f64_rad2deg, + col.nrows(), col.stype() + )); + } + return Column(new FuncUnary1_ColumnImpl( + std::move(col), f32_rad2deg, + col.nrows(), col.stype() + )); + + } + } + +}; + +static py::oobj pyfn_sin(const py::XArgs &args) { + auto arg = args[0].to_oobj(); + return PyFExpr::make(new FExpr_Trigonometric<1>(as_fexpr(arg))); } - - - -//------------------------------------------------------------------------------ -// Op::ARCCOS -//------------------------------------------------------------------------------ - -py::PKArgs args_arccos(1, 0, 0, false, false, {"x"}, "arccos", dt::doc_math_arccos); - - -umaker_ptr resolve_op_arccos(SType stype) { - return _resolve_trig(stype, "arccos", &std::acos, &std::acos); +static py::oobj pyfn_cos(const py::XArgs &args) { + auto arg = args[0].to_oobj(); + return PyFExpr::make(new FExpr_Trigonometric<2>(as_fexpr(arg))); } - - - -//------------------------------------------------------------------------------ -// Op::ARCTAN -//------------------------------------------------------------------------------ - -py::PKArgs args_arctan(1, 0, 0, false, false, {"x"}, "arctan", dt::doc_math_arctan); - - -umaker_ptr resolve_op_arctan(SType stype) { - return _resolve_trig(stype, "arctan", &std::atan, &std::atan); +static py::oobj pyfn_tan(const py::XArgs &args) { + auto arg = args[0].to_oobj(); + return PyFExpr::make(new FExpr_Trigonometric<3>(as_fexpr(arg))); } - - - -//------------------------------------------------------------------------------ -// Op::DEG2RAD -//------------------------------------------------------------------------------ - -py::PKArgs args_deg2rad(1, 0, 0, false, false, {"x"}, "deg2rad", dt::doc_math_deg2rad); - - -static double f64_deg2rad(double x) { - static constexpr double RADIANS_IN_DEGREE = 0.017453292519943295; - return x * RADIANS_IN_DEGREE; +static py::oobj pyfn_arcsin(const py::XArgs &args) { + auto arg = args[0].to_oobj(); + return PyFExpr::make(new FExpr_Trigonometric<4>(as_fexpr(arg))); } -static float f32_deg2rad(float x) { - static constexpr float RADIANS_IN_DEGREE = 0.0174532924f; - return x * RADIANS_IN_DEGREE; +static py::oobj pyfn_arccos(const py::XArgs &args) { + auto arg = args[0].to_oobj(); + return PyFExpr::make(new FExpr_Trigonometric<5>(as_fexpr(arg))); } - -umaker_ptr resolve_op_deg2rad(SType stype) { - return _resolve_trig(stype, "deg2rad", f32_deg2rad, f64_deg2rad); +static py::oobj pyfn_arctan(const py::XArgs &args) { + auto arg = args[0].to_oobj(); + return PyFExpr::make(new FExpr_Trigonometric<6>(as_fexpr(arg))); } - - - -//------------------------------------------------------------------------------ -// Op::RAD2DEG -//------------------------------------------------------------------------------ - -py::PKArgs args_rad2deg(1, 0, 0, false, false, {"x"}, "rad2deg", dt::doc_math_rad2deg); - - -static double f64_rad2deg(double x) { - static constexpr double DEGREES_IN_RADIAN = 57.295779513082323; - return x * DEGREES_IN_RADIAN; +static py::oobj pyfn_deg2rad(const py::XArgs &args) { + auto arg = args[0].to_oobj(); + return PyFExpr::make(new FExpr_Trigonometric<7>(as_fexpr(arg))); } -static float f32_rad2deg(float x) { - static constexpr float DEGREES_IN_RADIAN = 57.2957802f; - return x * DEGREES_IN_RADIAN; +static py::oobj pyfn_rad2deg(const py::XArgs &args) { + auto arg = args[0].to_oobj(); + return PyFExpr::make(new FExpr_Trigonometric<8>(as_fexpr(arg))); } -umaker_ptr resolve_op_rad2deg(SType stype) { - return _resolve_trig(stype, "rad2deg", f32_rad2deg, f64_rad2deg); -} - - - +DECLARE_PYFN(&pyfn_sin) + ->name("sin") + ->docs(doc_math_sin) + ->arg_names({"cols"}) + ->n_positional_args(1) + ->n_required_args(1); + +DECLARE_PYFN(&pyfn_cos) + ->name("cos") + ->docs(doc_math_cos) + ->arg_names({"cols"}) + ->n_positional_args(1) + ->n_required_args(1); + +DECLARE_PYFN(&pyfn_tan) + ->name("tan") + ->docs(doc_math_tan) + ->arg_names({"cols"}) + ->n_positional_args(1) + ->n_required_args(1); + +DECLARE_PYFN(&pyfn_arcsin) + ->name("arcsin") + ->docs(doc_math_arcsin) + ->arg_names({"cols"}) + ->n_positional_args(1) + ->n_required_args(1); + +DECLARE_PYFN(&pyfn_arccos) + ->name("arccos") + ->docs(doc_math_arccos) + ->arg_names({"cols"}) + ->n_positional_args(1) + ->n_required_args(1); + +DECLARE_PYFN(&pyfn_arctan) + ->name("arctan") + ->docs(doc_math_arctan) + ->arg_names({"cols"}) + ->n_positional_args(1) + ->n_required_args(1); + +DECLARE_PYFN(&pyfn_deg2rad) + ->name("deg2rad") + ->docs(doc_math_deg2rad) + ->arg_names({"cols"}) + ->n_positional_args(1) + ->n_required_args(1); + +DECLARE_PYFN(&pyfn_rad2deg) + ->name("rad2deg") + ->docs(doc_math_rad2deg) + ->arg_names({"cols"}) + ->n_positional_args(1) + ->n_required_args(1); + +}} // dt::expr -}} // namespace dt::expr