diff --git a/examples/tensor/simple_expressions.cpp b/examples/tensor/simple_expressions.cpp index 81c6e1cf8..5b3cab68d 100644 --- a/examples/tensor/simple_expressions.cpp +++ b/examples/tensor/simple_expressions.cpp @@ -65,6 +65,53 @@ int main() std::cout << "% --------------------------- " << std::endl << std::endl; std::cout << "F=" << F << ";" << std::endl << std::endl; + // Calling overloaded operators + // and mixing expression templates with prvalues, rvalues, and lvalues + { + // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) + auto G = tensor(shape{3,3}, 3.f); + auto E_9 = G + G + G; + + // formatted output + std::cout << "% --------------------------- " << std::endl; + std::cout << "% --------------------------- " << std::endl << std::endl; + std::cout << "E(9)=" << tensor(E_9) << ";" << std::endl << std::endl; + + // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) + auto E_6 = G + 3.f; + + // formatted output + std::cout << "% --------------------------- " << std::endl; + std::cout << "% --------------------------- " << std::endl << std::endl; + std::cout << "E(6)=" << tensor(E_6) << ";" << std::endl << std::endl; + + // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) + auto const four = 4.f; + auto E_10 = E_6 + four; + + // formatted output + std::cout << "% --------------------------- " << std::endl; + std::cout << "% --------------------------- " << std::endl << std::endl; + std::cout << "E(10)=" << tensor(E_10) << ";" << std::endl << std::endl; + + // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) + auto E_23 = E_10 + E_10 + tensor(shape{3,3}, 3.f); + + // formatted output + std::cout << "% --------------------------- " << std::endl; + std::cout << "% --------------------------- " << std::endl << std::endl; + std::cout << "E(23)=" << tensor(E_23) << ";" << std::endl << std::endl; + + // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) + auto E_9_7 = tensor(shape{3,3}, 5.4f) + 4.3f; + + // formatted output + std::cout << "% --------------------------- " << std::endl; + std::cout << "% --------------------------- " << std::endl << std::endl; + std::cout << "E(9.7)=" << tensor(E_9_7) << ";" << std::endl << std::endl; + + } + } catch (const std::exception& e) { std::cerr << "Cought exception " << e.what(); std::cerr << "in the main function of simple expression." << std::endl; diff --git a/include/boost/numeric/ublas/tensor/expression.hpp b/include/boost/numeric/ublas/tensor/expression.hpp index 47d534010..31893c1d1 100644 --- a/include/boost/numeric/ublas/tensor/expression.hpp +++ b/include/boost/numeric/ublas/tensor/expression.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2018-2019, Cem Bassoy, cem.bassoy@gmail.com +// Copyright (c) 2022, Amit Singh, amitsingh19975@gmail.com // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -13,6 +14,7 @@ #define BOOST_UBLAS_TENSOR_EXPRESSIONS_HPP #include +#include #include #include "tags.hpp" @@ -20,6 +22,87 @@ namespace boost::numeric::ublas::detail { +template +struct tensor_expression; + +template +struct binary_tensor_expression; + +template +struct unary_tensor_expression; + +template +concept same_exp = std::is_same_v< std::decay_t, std::decay_t >; + +template +struct does_exp_need_cast : std::false_type{}; + +template +static constexpr bool does_exp_need_cast_v = does_exp_need_cast< std::decay_t >::value; + +template +struct does_exp_need_cast< tensor_expression > : std::true_type{}; + +/** + * @brief It is a safer way of casting `tensor_expression` because it handles + * recursive expressions. Otherwise, in most of the cases, we try to access + * `operator()`, which requires a parameter argument, that is not supported + * by the `tensor_expression` class and might give an error if it is not casted + * properly. + * + * @tparam T type of the tensor + * @tparam E type of the child stored inside tensor_expression + * @param e tensor_expression that needs to be casted + * @return child of tensor_expression that is not tensor_expression + */ +template +[[nodiscard]] constexpr auto const& cast_tensor_expression(tensor_expression const& e) noexcept{ + auto const& res = e(); + if constexpr(does_exp_need_cast_v) + return cast_tensor_expression(res); + else + return res; +} + + +// FIXME: remove it when template expression support for the old matrix and vector is removed +/// @brief No Op: Any expression other than `tensor_expression`. +template +[[nodiscard]] constexpr auto const& cast_tensor_expression(E const& e) noexcept{ return e; } + +template +constexpr auto is_tensor_expression_impl(tensor_expression const*) -> std::true_type; + +constexpr auto is_tensor_expression_impl(void const*) -> std::false_type; + +template +constexpr auto is_matrix_expression_impl(matrix_expression const*) -> std::true_type; + +constexpr auto is_matrix_expression_impl(void const*) -> std::false_type; + +template +constexpr auto is_vector_expression_impl(vector_expression const*) -> std::true_type; + +constexpr auto is_vector_expression_impl(void const*) -> std::false_type; + +template +concept TensorExpression = decltype(is_tensor_expression_impl(static_cast const*>(nullptr)))::value; + +// TODO: Remove this concept in the future when we have our own implementation of matrices. +template +concept MatrixExpression = decltype(is_matrix_expression_impl(static_cast const*>(nullptr)))::value; + +// TODO: Remove this concept in the future when we have our own implementation of vectors. +template +concept VectorExpression = decltype(is_vector_expression_impl(static_cast const*>(nullptr)))::value; + +template +using expression_operand_t = std::conditional_t< + !std::is_lvalue_reference_v, + std::decay_t, + std::add_lvalue_reference_t< std::add_const_t< std::decay_t > > +>; + /** @\brief base class for tensor expressions * * \note implements crtp - no use of virtual function calls @@ -30,7 +113,7 @@ namespace boost::numeric::ublas::detail **/ template struct tensor_expression - : public ublas_expression + : public ublas_expression // DISCUSS: Do we really need to do derive from ublas_expression? { // static const unsigned complexity = 0; using expression_type = E; @@ -39,16 +122,32 @@ struct tensor_expression inline constexpr auto const& operator()() const noexcept { return *static_cast (this); } - - ~tensor_expression() = default; + + /// @brief The copy constructor is deleted to avoid expensive copies or sometimes object slicing. tensor_expression(const tensor_expression&) = delete; - tensor_expression(tensor_expression&&) noexcept = delete; tensor_expression& operator=(const tensor_expression&) = delete; - tensor_expression& operator=(tensor_expression&&) noexcept = delete; - + + constexpr tensor_expression& operator=(tensor_expression&&) noexcept = delete; + + constexpr ~tensor_expression() = default; protected : + /** + * @brief This is the only way to discourage the users from using `std::move` on the local + * expression because it works differently from the standard way to move the objects. This weird + * behaviour is due to `const reference`, which is impossible to move without constructing a new object. + * If the variable goes out of the scope, stored as a `const reference` inside the expression, + * it will be destroyed that will result in a dangling pointer. But this behaviour is helpful + * for the construction of an expression because the expression might contain a `const reference` + * object that will be passed around as a `const reference` rather than a copy, and we do not need to + * construct a whole new chunky object because, under the hood, we are just passing pointers around. + * + */ + constexpr tensor_expression(tensor_expression&&) noexcept = default; explicit tensor_expression() = default; + + /// @brief This is the only way to access the protected move constructor of other expressions. + template friend struct tensor_expression; }; @@ -58,72 +157,76 @@ struct binary_tensor_expression { using self_type = binary_tensor_expression; using tensor_type = T; - using binary_operation = OP; - using expression_type_left = EL; - using expression_type_right = ER; + using binary_operation = std::decay_t; + using expression_type_left = expression_operand_t; + using expression_type_right = expression_operand_t; using derived_type = tensor_expression ; using size_type = typename tensor_type::size_type; - explicit constexpr binary_tensor_expression(expression_type_left const& l, expression_type_right const& r, binary_operation o) : el(l) , er(r) , op(std::move(o)) {} - constexpr binary_tensor_expression(binary_tensor_expression&& l) noexcept = delete; - constexpr binary_tensor_expression& operator=(binary_tensor_expression&& l) noexcept = delete; - ~binary_tensor_expression() = default; - binary_tensor_expression() = delete; + + template LeftExp, same_exp RightExp, typename OPType> + explicit constexpr binary_tensor_expression(LeftExp&& l, RightExp&& r, OPType&& o) + : el(std::forward(l)) + , er(std::forward(r)) + , op(std::forward(o)) + {} + + /// @brief The copy constructor is deleted to avoid expensive copies or sometimes object slicing. binary_tensor_expression(const binary_tensor_expression& l) = delete; binary_tensor_expression& operator=(binary_tensor_expression const& l) noexcept = delete; + + constexpr binary_tensor_expression& operator=(binary_tensor_expression&& l) noexcept = delete; + + constexpr ~binary_tensor_expression() = default; + [[nodiscard]] constexpr auto const& left_expr() const noexcept{ return cast_tensor_expression(el); } + [[nodiscard]] constexpr auto const& right_expr() const noexcept{ return cast_tensor_expression(er); } + [[nodiscard]] inline constexpr + decltype(auto) operator()(size_type i) const { return std::invoke(op, left_expr()(i), right_expr()(i)); } + +protected: + /** + * @brief This is the only way to discourage the users from using `std::move` on the local + * expression because it works differently from the standard way to move the objects. This weird + * behaviour is due to `const reference`, which is impossible to move without constructing a new object. + * If the variable goes out of the scope, stored as a `const reference` inside the expression, + * it will be destroyed that will result in a dangling pointer. But this behaviour is helpful + * for the construction of an expression because the expression might contain a `const reference` + * object that will be passed around as a `const reference` rather than a copy, and we do not need to + * construct a whole new chunky object because, under the hood, we are just passing pointers around. + * + */ + constexpr binary_tensor_expression(binary_tensor_expression&& l) noexcept = default; + + /// @brief This is the only way to access the protected move constructor of other expressions. + template friend struct unary_tensor_expression; + template friend struct binary_tensor_expression; - [[nodiscard]] inline - constexpr decltype(auto) operator()(size_type i) const { return op(el(i), er(i)); } - - expression_type_left const& el; - expression_type_right const& er; +private: + expression_type_left el; + expression_type_right er; binary_operation op; }; /// @brief helper function to simply instantiation of lambda proxy class -template +template + requires ( + ( MatrixExpression || VectorExpression || TensorExpression ) && + ( MatrixExpression || VectorExpression || TensorExpression ) + ) [[nodiscard]] inline -constexpr auto make_binary_tensor_expression( tensor_expression const& el, tensor_expression const& er, OP op) noexcept +constexpr auto make_binary_tensor_expression( EL&& el, ER&& er, OP&& op) noexcept { - static_assert( std::is_same_v< typename T1::value_type, typename T2::value_type>, - "boost::numeric::ublas::make_binary_tensor_expression(T1,T2) : LHS tensor and RHS tensor should have same value type" + return binary_tensor_expression( + std::forward(el), + std::forward(er), + std::forward(op) ); - return binary_tensor_expression( el(), er(), op) ; } -template -[[nodiscard]] inline -constexpr auto make_binary_tensor_expression( matrix_expression const& el, tensor_expression const& er, OP op) noexcept -{ - return binary_tensor_expression( el(), er(), op) ; -} - -template -[[nodiscard]] inline -constexpr auto make_binary_tensor_expression( tensor_expression const& el, matrix_expression const& er, OP op) noexcept -{ - return binary_tensor_expression( el(), er(), op) ; -} - -template -[[nodiscard]] inline -constexpr auto make_binary_tensor_expression( vector_expression const& el, tensor_expression const& er, OP op) noexcept -{ - return binary_tensor_expression( el(), er(), op) ; -} - -template -[[nodiscard]] inline -constexpr auto make_binary_tensor_expression( tensor_expression const& el, vector_expression const& er, OP op) noexcept -{ - return binary_tensor_expression( el(), er(), op) ; -} - - template struct unary_tensor_expression @@ -132,51 +235,67 @@ struct unary_tensor_expression using self_type = unary_tensor_expression; using tensor_type = T; - using expression_type = E; - using unary_operation = OP; + using expression_type = expression_operand_t; + using unary_operation = std::decay_t; using derived_type = tensor_expression >; using size_type = typename tensor_type::size_type; - - explicit constexpr unary_tensor_expression(expression_type const& ee, unary_operation o) : e(ee) , op(std::move(o)) {} - constexpr unary_tensor_expression(unary_tensor_expression&& l) noexcept = delete; - constexpr unary_tensor_expression& operator=(unary_tensor_expression&& l) noexcept = delete; - + constexpr unary_tensor_expression() = delete; + + template Exp, typename OPType> + explicit constexpr unary_tensor_expression(Exp&& ee, OPType&& o) + : e(std::forward(ee)) + , op(std::forward(o)) + {} + + /// @brief The copy constructor is deleted to avoid expensive copies or sometimes object slicing. unary_tensor_expression(unary_tensor_expression const& l) = delete; unary_tensor_expression& operator=(unary_tensor_expression const& l) noexcept = delete; - ~unary_tensor_expression() = default; - [[nodiscard]] inline constexpr - decltype(auto) operator()(size_type i) const { return op(e(i)); } + constexpr unary_tensor_expression& operator=(unary_tensor_expression&& l) noexcept = delete; + constexpr ~unary_tensor_expression() = default; + + [[nodiscard]] constexpr auto const& expr() const noexcept{ return cast_tensor_expression(e); } - E const& e; - OP op; + [[nodiscard]] inline constexpr + decltype(auto) operator()(size_type i) const { return std::invoke(op, expr()(i)); } + +protected: + /** + * @brief This is the only way to discourage the users from using `std::move` on the local + * expression because it works differently from the standard way to move the objects. This weird + * behaviour is due to `const reference`, which is impossible to move without constructing a new object. + * If the variable goes out of the scope, stored as a `const reference` inside the expression, + * it will be destroyed that will result in a dangling pointer. But this behaviour is helpful + * for the construction of an expression because the expression might contain a `const reference` + * object that will be passed around as a `const reference` rather than a copy, and we do not need to + * construct a whole new chunky object because, under the hood, we are just passing pointers around. + * + */ + constexpr unary_tensor_expression(unary_tensor_expression&& l) noexcept = default; + + /// @brief This is the only way to access the protected move constructor of other expressions. + template friend struct unary_tensor_expression; + template friend struct binary_tensor_expression; + +private: + expression_type e; + unary_operation op; }; // \brief helper function to simply instantiation of lambda proxy class -template +template + requires ( MatrixExpression || VectorExpression || TensorExpression ) [[nodiscard]] inline -constexpr auto make_unary_tensor_expression( tensor_expression const& e, OP op) noexcept +constexpr auto make_unary_tensor_expression( E&& e, OP&& op) noexcept { - return unary_tensor_expression( e() , op); -} - -template -[[nodiscard]] inline -constexpr auto make_unary_tensor_expression( matrix_expression const& e, OP op) noexcept -{ - return unary_tensor_expression( e() , op); -} - -template -[[nodiscard]] inline -constexpr auto make_unary_tensor_expression( vector_expression const& e, OP op) noexcept -{ - return unary_tensor_expression( e() , op); + return unary_tensor_expression( + std::forward(e), + std::forward(op) + ); } - } // namespace boost::numeric::ublas::detail #endif // BOOST_UBLAS_TENSOR_EXPRESSIONS_HPP diff --git a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp index d29b6eabe..9269c6e32 100644 --- a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp +++ b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp @@ -1,6 +1,6 @@ // // Copyright (c) 2018, Cem Bassoy, cem.bassoy@gmail.com -// Copyright (c) 2019, Amit Singh, amitsingh19975@gmail.com +// Copyright (c) 2019-2022, Amit Singh, amitsingh19975@gmail.com // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -45,25 +45,41 @@ struct unary_tensor_expression; namespace boost::numeric::ublas::detail { -template -struct has_tensor_types -{ static constexpr bool value = false; }; +template +struct is_tensor_type + : std::false_type +{}; + +template +struct is_tensor_type< tensor_core > + : std::true_type +{}; + +template +static constexpr bool is_tensor_type_v = is_tensor_type< std::decay_t >::value; + +template +struct has_tensor_types + : is_tensor_type +{}; template -struct has_tensor_types -{ static constexpr bool value = true; }; +static constexpr bool has_tensor_types_v = has_tensor_types< std::decay_t >::value; template -struct has_tensor_types> -{ static constexpr bool value = std::is_same::value || has_tensor_types::value; }; +struct has_tensor_types< tensor_expression > + : has_tensor_types< std::decay_t > +{}; template -struct has_tensor_types> -{ static constexpr bool value = std::is_same::value || std::is_same::value || has_tensor_types::value || has_tensor_types::value; }; +struct has_tensor_types< binary_tensor_expression > + : std::integral_constant< bool, has_tensor_types_v || has_tensor_types_v > +{}; template -struct has_tensor_types> -{ static constexpr bool value = std::is_same::value || has_tensor_types::value; }; +struct has_tensor_types< unary_tensor_expression > + : has_tensor_types< std::decay_t > +{}; } // namespace boost::numeric::ublas::detail @@ -71,12 +87,19 @@ struct has_tensor_types> namespace boost::numeric::ublas::detail { + +// TODO: remove this place holder for the old ublas expression after we remove the +// support for them. +template +[[nodiscard]] +constexpr auto& retrieve_extents([[maybe_unused]] ublas_expression const& /*unused*/) noexcept; + /** @brief Retrieves extents of the tensor_core * */ template [[nodiscard]] -constexpr auto& retrieve_extents(tensor_core const& t) +constexpr auto& retrieve_extents(tensor_core const& t) noexcept { return t.extents(); } @@ -89,16 +112,13 @@ constexpr auto& retrieve_extents(tensor_core const& t) */ template [[nodiscard]] -constexpr auto& retrieve_extents(tensor_expression const& expr) +constexpr auto& retrieve_extents(tensor_expression const& expr) noexcept { - static_assert(detail::has_tensor_types>::value, + static_assert(has_tensor_types_v>, "Error in boost::numeric::ublas::detail::retrieve_extents: Expression to evaluate should contain tensors."); - auto const& cast_expr = static_cast(expr); - - if constexpr ( std::is_same::value ) - return cast_expr.extents(); - else + auto const& cast_expr = expr(); + return retrieve_extents(cast_expr); } @@ -115,22 +135,25 @@ constexpr auto& retrieve_extents(tensor_expression const& expr) */ template [[nodiscard]] -constexpr auto& retrieve_extents(binary_tensor_expression const& expr) +constexpr auto& retrieve_extents(binary_tensor_expression const& expr) noexcept { - static_assert(detail::has_tensor_types>::value, + static_assert(has_tensor_types_v>, "Error in boost::numeric::ublas::detail::retrieve_extents: Expression to evaluate should contain tensors."); - if constexpr ( std::is_same::value ) - return expr.el.extents(); + auto const& lexpr = expr.left_expr(); + auto const& rexpr = expr.right_expr(); - if constexpr ( std::is_same::value ) - return expr.er.extents(); + if constexpr ( is_tensor_type_v ) + return lexpr.extents(); - else if constexpr ( detail::has_tensor_types::value ) - return retrieve_extents(expr.el); + else if constexpr ( is_tensor_type_v ) + return rexpr.extents(); - else if constexpr ( detail::has_tensor_types::value ) - return retrieve_extents(expr.er); + else if constexpr ( has_tensor_types_v) + return retrieve_extents(lexpr); + + else if constexpr ( has_tensor_types_v) + return retrieve_extents(rexpr); } #ifdef _MSC_VER @@ -145,17 +168,15 @@ constexpr auto& retrieve_extents(binary_tensor_expression const& exp */ template [[nodiscard]] -constexpr auto& retrieve_extents(unary_tensor_expression const& expr) +constexpr auto& retrieve_extents(unary_tensor_expression const& expr) noexcept { - static_assert(detail::has_tensor_types>::value, + static_assert(has_tensor_types_v>, "Error in boost::numeric::ublas::detail::retrieve_extents: Expression to evaluate should contain tensors."); + + auto const& uexpr = expr.expr(); - if constexpr ( std::is_same::value ) - return expr.e.extents(); - - else if constexpr ( detail::has_tensor_types::value ) - return retrieve_extents(expr.e); + return retrieve_extents(uexpr); } } // namespace boost::numeric::ublas::detail @@ -165,86 +186,60 @@ constexpr auto& retrieve_extents(unary_tensor_expression const& expr) namespace boost::numeric::ublas::detail { +// TODO: remove this place holder for the old ublas expression after we remove the +// support for them. +template +[[nodiscard]] inline + constexpr auto all_extents_equal([[maybe_unused]] ublas_expression const& /*unused*/, [[maybe_unused]] extents const& /*unused*/) noexcept +{ + return true; +} + template [[nodiscard]] inline - constexpr auto all_extents_equal(tensor_core const& t, extents const& e) + constexpr auto all_extents_equal(tensor_core const& t, extents const& e) noexcept { return ::operator==(e,t.extents()); } template [[nodiscard]] -constexpr auto all_extents_equal(tensor_expression const& expr, extents const& e) +constexpr auto all_extents_equal(tensor_expression const& expr, extents const& e) noexcept { - static_assert(detail::has_tensor_types>::value, - "Error in boost::numeric::ublas::detail::all_extents_equal: Expression to evaluate should contain tensors."); - - auto const& cast_expr = static_cast(expr); - - using ::operator==; - using ::operator!=; + static_assert(has_tensor_types_v>, + "Error in boost::numeric::ublas::all_extents_equal: Expression to evaluate should contain tensors."); - if constexpr ( std::is_same::value ) - if( e != cast_expr.extents() ) - return false; - - if constexpr ( detail::has_tensor_types::value ) - if ( !all_extents_equal(cast_expr, e)) - return false; - - return true; + auto const& cast_expr = expr(); + return all_extents_equal(cast_expr, e); } template [[nodiscard]] -constexpr auto all_extents_equal(binary_tensor_expression const& expr, extents const& e) +constexpr auto all_extents_equal(binary_tensor_expression const& expr, extents const& e) noexcept { - static_assert(detail::has_tensor_types>::value, - "Error in boost::numeric::ublas::detail::all_extents_equal: Expression to evaluate should contain tensors."); - - using ::operator==; - using ::operator!=; - - if constexpr ( std::is_same::value ) - if(e != expr.el.extents()) - return false; - - if constexpr ( std::is_same::value ) - if(e != expr.er.extents()) - return false; + static_assert(has_tensor_types_v>, + "Error in boost::numeric::ublas::all_extents_equal: Expression to evaluate should contain tensors."); - if constexpr ( detail::has_tensor_types::value ) - if(!all_extents_equal(expr.el, e)) - return false; + auto const& lexpr = expr.left_expr(); + auto const& rexpr = expr.right_expr(); - if constexpr ( detail::has_tensor_types::value ) - if(!all_extents_equal(expr.er, e)) - return false; - - return true; + return all_extents_equal(lexpr, e) && + all_extents_equal(rexpr, e) ; } template [[nodiscard]] -constexpr auto all_extents_equal(unary_tensor_expression const& expr, extents const& e) +constexpr auto all_extents_equal(unary_tensor_expression const& expr, extents const& e) noexcept { - static_assert(detail::has_tensor_types>::value, - "Error in boost::numeric::ublas::detail::all_extents_equal: Expression to evaluate should contain tensors."); - - using ::operator==; - - if constexpr ( std::is_same::value ) - if(e != expr.e.extents()) - return false; + static_assert(has_tensor_types_v>, + "Error in boost::numeric::ublas::all_extents_equal: Expression to evaluate should contain tensors."); - if constexpr ( detail::has_tensor_types::value ) - if(!all_extents_equal(expr.e, e)) - return false; + auto const& uexpr = expr.expr(); - return true; + return all_extents_equal(uexpr, e); } } // namespace boost::numeric::ublas::detail @@ -253,72 +248,79 @@ constexpr auto all_extents_equal(unary_tensor_expression const& expr, ex namespace boost::numeric::ublas::detail { - /** @brief Evaluates expression for a tensor_core * - * Assigns the results of the expression to the tensor_core. + * Applies a binary function to the results of the expressions before the assignment. + * Usually applied needed for binary operators such as A += C; * * \note Checks if shape of the tensor_core matches those of all tensors within the expression. */ -template -inline void eval(tensor_type& lhs, tensor_expression const& expr) +template + requires + std::is_invocable_r_v::reference, + typename tensor_core::const_reference + > +inline void eval(tensor_core& lhs, TensorExpression auto const& expr, BinaryFn&& fn) + noexcept( + is_static_v< std::decay_t< decltype(retrieve_extents(lhs)) > > && + is_static_v< std::decay_t< decltype(retrieve_extents(expr)) > > + ) { - if constexpr (detail::has_tensor_types >::value ) - if(!detail::all_extents_equal(expr, lhs.extents() )) - throw std::runtime_error("Error in boost::numeric::ublas::tensor_core: expression contains tensors with different shapes."); + auto const& rhs = cast_tensor_expression(expr); -#pragma omp parallel for - for(auto i = 0u; i < lhs.size(); ++i) - lhs(i) = expr()(i); -} + using ltensor_t = tensor_core; + using lvalue_type = typename ltensor_t::value_type; + using lextents_t = typename ltensor_t::extents_type; + using rvalue_type = std::decay_t< decltype(rhs(0)) >; + using rextents_t = std::decay_t< decltype(retrieve_extents(expr)) >; -/** @brief Evaluates expression for a tensor_core - * - * Assigns the results of the expression to the tensor_core. - * - * \note Checks if shape of the tensor_core matches those of all tensors within the expression. -*/ -template -inline void eval(tensor_type& lhs, tensor_expression const& expr) -{ - -// static_assert(is_valid_tensor_v && is_valid_tensor_v, -// "boost::numeric::ublas::detail::eval(tensor_type&, tensor_expression const&) : " -// "tensor_type and tensor_expresssion should be a valid tensor type" -// ); - - static_assert(std::is_same_v, - "boost::numeric::ublas::detail::eval(tensor_type&, tensor_expression const&) : " - "tensor_type and tensor_expresssion should have same value type" + static_assert(std::is_same_v, + "boost::numeric::ublas::detail::eval(tensor_core& lhs, tensor_expresion const& rhs, BinaryFn&& fn) : " + "both LHS and RHS tensors should have same value type" ); - if ( !detail::all_extents_equal(expr, lhs.extents() ) ){ - throw std::runtime_error("Error in boost::numeric::ublas::tensor_core: expression contains tensors with different shapes."); - } - + if constexpr(is_static_v && is_static_v){ + static_assert(std::is_same_v, + "boost::numeric::ublas::detail::eval(tensor_core& lhs, tensor_expresion const& rhs, BinaryFn&& fn) : " + "both LHS and RHS tensors should have same shape." + ); + }else{ + if ( !all_extents_equal( expr, lhs.extents() ) ){ + throw std::runtime_error( + "boost::numeric::ublas::detail::eval(tensor_core& lhs, tensor_expresion const& rhs, BinaryFn&& fn) : " + "both LHS and RHS tensors should have same shape." + ); + } + } + + auto const size = lhs.size(); + + /// FIXME: add 'simd' clause and 'if' clause that will be used as a starting point + /// for threads to start, otherwise, it's very expansive to use threads for small + /// sized containers. + /// @code #pragma omp parallel for simd if(size > SOME_SIZE) @endcode #pragma omp parallel for - for(auto i = 0u; i < lhs.size(); ++i) - lhs(i) = expr()(i); + for(auto i = 0u; i < size; ++i) + std::invoke(fn, lhs(i), rhs(i)); } /** @brief Evaluates expression for a tensor_core * - * Applies a unary function to the results of the expressions before the assignment. - * Usually applied needed for unary operators such as A += C; + * Assigns the results of the expression to the tensor_core. * * \note Checks if shape of the tensor_core matches those of all tensors within the expression. */ -template -inline void eval(tensor_type& lhs, tensor_expression const& expr, unary_fn const fn) +template +inline void eval(tensor_core& lhs, TensorExpression auto const& expr) + noexcept( + is_static_v< std::decay_t< decltype(retrieve_extents(lhs)) > > && + is_static_v< std::decay_t< decltype(retrieve_extents(expr)) > > + ) { + using value_type = typename tensor_core::value_type; - if constexpr (detail::has_tensor_types< tensor_type, tensor_expression >::value ) - if(!detail::all_extents_equal( expr, lhs.extents() )) - throw std::runtime_error("Error in boost::numeric::ublas::tensor_core: expression contains tensors with different shapes."); - - #pragma omp parallel for - for(auto i = 0u; i < lhs.size(); ++i) - fn(lhs(i), expr()(i)); + eval(lhs, expr, [](value_type& l, value_type const& r){ l = r; }); } @@ -330,12 +332,20 @@ inline void eval(tensor_type& lhs, tensor_expression * * \note Checks if shape of the tensor_core matches those of all tensors within the expression. */ -template -inline void eval(tensor_type& lhs, unary_fn const& fn) +template + requires std::is_invocable_r_v::reference> +inline void eval(tensor_core& lhs, UnaryFn&& fn) + noexcept( is_static_v< std::decay_t< decltype(retrieve_extents(lhs)) > > ) { -#pragma omp parallel for - for(auto i = 0u; i < lhs.size(); ++i) - fn(lhs(i)); + auto const size = lhs.size(); + + /// FIXME: add 'simd' clause and 'if' clause that will be used as a starting point + /// for threads to start, otherwise, it's very expansive to use threads for small + /// sized containers. + /// @code #pragma omp parallel for simd if(size > SOME_SIZE) @endcode + #pragma omp parallel for + for(auto i = 0u; i < size; ++i) + std::invoke(fn, lhs(i)); } diff --git a/include/boost/numeric/ublas/tensor/multiplication.hpp b/include/boost/numeric/ublas/tensor/multiplication.hpp index 6a9c0613b..9fc5abb95 100644 --- a/include/boost/numeric/ublas/tensor/multiplication.hpp +++ b/include/boost/numeric/ublas/tensor/multiplication.hpp @@ -389,7 +389,7 @@ void mtv(SizeType const m, template void mtm(PointerOut c, SizeType const*const nc, SizeType const*const wc, PointerIn1 a, SizeType const*const na, SizeType const*const wa, - PointerIn2 b, SizeType const*const nb, SizeType const*const wb) + PointerIn2 b, [[maybe_unused]] SizeType const*const nb, SizeType const*const wb) { // C(i,j) = A(i,k) * B(k,j) diff --git a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp index fa89d431f..8694a1cb4 100644 --- a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp +++ b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2018, Cem Bassoy, cem.bassoy@gmail.com +// Copyright (c) 2022, Amit Singh, amitsingh19975@gmail.com // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -35,359 +36,412 @@ class matrix_expression; template class vector_expression; +namespace detail{ + + template + struct real_expression_type { + using type = E; + }; + + template + struct real_expression_type< tensor_expression > { + using type = T; + }; + + template + struct real_expression_type< binary_tensor_expression > { + using type = T; + }; + + template + struct real_expression_type< unary_tensor_expression > { + using type = T; + }; + + template + using real_expression_type_t = typename real_expression_type< std::decay_t >::type; + +} // namespace detail + + } // namespace boost::numeric::ublas -template +template + requires ( + (boost::numeric::ublas::detail::TensorExpression) && + (boost::numeric::ublas::detail::VectorExpression || boost::numeric::ublas::detail::MatrixExpression) + ) inline - constexpr auto operator*( - boost ::numeric ::ublas ::detail ::tensor_expression const& lhs, - boost ::numeric ::ublas ::vector_expression const& rhs) noexcept + constexpr auto operator*( EL&& lhs, ER&& rhs ) noexcept { - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::multiplies<>{}); -} + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; -template -inline - constexpr auto operator+( - boost ::numeric ::ublas ::detail ::tensor_expression const& lhs, - boost ::numeric ::ublas ::vector_expression const& rhs) noexcept -{ - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::plus<>{}); + return boost::numeric::ublas::detail::make_binary_tensor_expression( + std::forward(lhs), std::forward(rhs), std::multiplies{} + ); } -template +template + requires ( + (boost::numeric::ublas::detail::TensorExpression) && + (boost::numeric::ublas::detail::VectorExpression || boost::numeric::ublas::detail::MatrixExpression) + ) inline - constexpr auto operator-( - boost ::numeric ::ublas ::detail ::tensor_expression const& lhs, - boost ::numeric ::ublas ::vector_expression const& rhs) noexcept + constexpr auto operator+( EL&& lhs, ER&& rhs ) noexcept { - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::minus<>{}); -} + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; -template -inline - constexpr auto operator/( - boost ::numeric ::ublas ::detail ::tensor_expression const& lhs, - boost ::numeric ::ublas ::vector_expression const& rhs) noexcept -{ - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::divides<>{}); + return boost::numeric::ublas::detail::make_binary_tensor_expression( + std::forward(lhs), std::forward(rhs), std::plus{} + ); } - -template +template + requires ( + (boost::numeric::ublas::detail::TensorExpression) && + (boost::numeric::ublas::detail::VectorExpression || boost::numeric::ublas::detail::MatrixExpression) + ) inline - constexpr auto operator*( - boost ::numeric ::ublas ::detail ::tensor_expression const& lhs, - boost ::numeric ::ublas ::matrix_expression const& rhs) noexcept + constexpr auto operator-( EL&& lhs, ER&& rhs ) noexcept { - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::multiplies<>{}); -} + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; -template -inline - constexpr auto operator+( - boost ::numeric ::ublas ::detail ::tensor_expression const& lhs, - boost ::numeric ::ublas ::matrix_expression const& rhs) noexcept -{ - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::plus<>{}); + return boost::numeric::ublas::detail::make_binary_tensor_expression( + std::forward(lhs), std::forward(rhs), std::minus{} + ); } -template +template + requires ( + (boost::numeric::ublas::detail::TensorExpression) && + (boost::numeric::ublas::detail::VectorExpression || boost::numeric::ublas::detail::MatrixExpression) + ) inline - constexpr auto operator-( - boost ::numeric ::ublas ::detail ::tensor_expression const& lhs, - boost ::numeric ::ublas ::matrix_expression const& rhs) noexcept + constexpr auto operator/( EL&& lhs, ER&& rhs ) noexcept { - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::minus<>{}); -} + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; -template -inline - constexpr auto operator/( - boost ::numeric ::ublas ::detail ::tensor_expression const& lhs, - boost ::numeric ::ublas ::matrix_expression const& rhs) noexcept -{ - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::divides<>{}); + return boost::numeric::ublas::detail::make_binary_tensor_expression( + std::forward(lhs), std::forward(rhs), std::divides{} + ); } - -template +template + requires ( + (boost::numeric::ublas::detail::VectorExpression || boost::numeric::ublas::detail::MatrixExpression) && + (boost::numeric::ublas::detail::TensorExpression) + ) inline - constexpr auto operator*( - boost ::numeric ::ublas ::vector_expression const& lhs, - boost ::numeric ::ublas ::detail ::tensor_expression const& rhs) noexcept + constexpr auto operator*( EL&& lhs, ER&& rhs ) noexcept { - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::multiplies<>{}); -} + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; -template -inline - constexpr auto operator+( - boost ::numeric ::ublas ::vector_expression const& lhs, - boost ::numeric ::ublas ::detail ::tensor_expression const& rhs) noexcept -{ - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::plus<>{}); + return boost::numeric::ublas::detail::make_binary_tensor_expression( + std::forward(lhs), std::forward(rhs), std::multiplies{} + ); } -template +template + requires ( + (boost::numeric::ublas::detail::VectorExpression || boost::numeric::ublas::detail::MatrixExpression) && + (boost::numeric::ublas::detail::TensorExpression) + ) inline - constexpr auto operator-( - boost ::numeric ::ublas ::vector_expression const& lhs, - boost ::numeric ::ublas ::detail ::tensor_expression const& rhs) noexcept + constexpr auto operator+( EL&& lhs, ER&& rhs ) noexcept { - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::minus<>{}); -} + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; -template -inline - constexpr auto operator/( - boost ::numeric ::ublas ::vector_expression const& lhs, - boost ::numeric ::ublas ::detail ::tensor_expression const& rhs) noexcept -{ - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::divides<>{}); + return boost::numeric::ublas::detail::make_binary_tensor_expression( + std::forward(lhs), std::forward(rhs), std::plus{} + ); } - -template +template + requires ( + (boost::numeric::ublas::detail::VectorExpression || boost::numeric::ublas::detail::MatrixExpression) && + (boost::numeric::ublas::detail::TensorExpression) + ) inline - constexpr auto operator*( - boost ::numeric ::ublas ::matrix_expression const& lhs, - boost ::numeric ::ublas ::detail ::tensor_expression const& rhs) noexcept + constexpr auto operator-( EL&& lhs, ER&& rhs ) noexcept { - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::multiplies<>{}); -} + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; -template -inline - constexpr auto operator+( - boost ::numeric ::ublas ::matrix_expression const& lhs, - boost ::numeric ::ublas ::detail ::tensor_expression const& rhs) noexcept -{ - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::plus<>{}); + return boost::numeric::ublas::detail::make_binary_tensor_expression( + std::forward(lhs), std::forward(rhs), std::minus{} + ); } -template -inline - constexpr auto operator-( - boost ::numeric ::ublas ::matrix_expression const& lhs, - boost ::numeric ::ublas ::detail ::tensor_expression const& rhs) noexcept +template + requires ( + (boost::numeric::ublas::detail::VectorExpression || boost::numeric::ublas::detail::MatrixExpression) && + (boost::numeric::ublas::detail::TensorExpression) + ) +inline constexpr auto operator/( EL&& lhs, ER&& rhs ) noexcept { - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::minus<>{}); -} + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; -template -inline - constexpr auto operator/( - boost ::numeric ::ublas ::matrix_expression const& lhs, - boost ::numeric ::ublas ::detail ::tensor_expression const& rhs) noexcept -{ - return boost ::numeric ::ublas ::detail ::make_binary_tensor_expression( - lhs(), rhs(), std::divides<>{}); + return boost::numeric::ublas::detail::make_binary_tensor_expression( + std::forward(lhs), std::forward(rhs), std::divides{} + ); } - -template -inline - constexpr auto operator+( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) +template + requires ( + (boost::numeric::ublas::detail::TensorExpression) && + (boost::numeric::ublas::detail::TensorExpression) + ) +inline constexpr auto operator+( EL&& lhs, ER&& rhs ) noexcept { - static_assert( std::is_same_v< typename T1::value_type, typename T2::value_type>, + using ltensor_t = boost::numeric::ublas::detail::real_expression_type_t; + using rtensor_t = boost::numeric::ublas::detail::real_expression_type_t; + using lvalue_t = typename ltensor_t::value_type; + using rvalue_t = typename rtensor_t::value_type; + + static_assert( std::is_same_v< lvalue_t, rvalue_t >, "operator+() : LHS tensor and RHS tensor should have the same value type" ); - if constexpr( !std::is_same_v ){ - auto const& e = boost::numeric::ublas::detail::retrieve_extents(rhs); + return boost::numeric::ublas::detail::make_binary_tensor_expression ( + std::forward(lhs), std::forward(rhs), std::plus{} + ); +} - if( !boost::numeric::ublas::detail::all_extents_equal(lhs,e) ){ - throw std::runtime_error("operator+() : LHS tensor and RHS tensor should have equal extents"); - } - } - return boost::numeric::ublas::detail::make_binary_tensor_expression (lhs(), rhs(), [](auto const& l, auto const& r){ return l + r; }); -} -template -inline - constexpr auto operator-( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) +template + requires ( + (boost::numeric::ublas::detail::TensorExpression) && + (boost::numeric::ublas::detail::TensorExpression) + ) +inline constexpr auto operator-( EL&& lhs, ER&& rhs ) noexcept { - static_assert( std::is_same_v< typename T1::value_type, typename T2::value_type>, + using ltensor_t = boost::numeric::ublas::detail::real_expression_type_t; + using rtensor_t = boost::numeric::ublas::detail::real_expression_type_t; + using lvalue_t = typename ltensor_t::value_type; + using rvalue_t = typename rtensor_t::value_type; + + static_assert( std::is_same_v< lvalue_t, rvalue_t >, "operator-() : LHS tensor and RHS tensor should have the same value type" ); - if constexpr( !std::is_same_v ){ - auto e = boost::numeric::ublas::detail::retrieve_extents(rhs); - - if( !boost::numeric::ublas::detail::all_extents_equal(lhs,e) ){ - throw std::runtime_error("operator+() : LHS tensor and RHS tensor should have equal extents"); - } - } - - return boost::numeric::ublas::detail::make_binary_tensor_expression (lhs(), rhs(), [](auto const& l, auto const& r){ return l - r; }); - // return boost::numeric::ublas::detail::make_lambda([&lhs,&rhs](std::size_t i){ return lhs(i) - rhs(i);}); + return boost::numeric::ublas::detail::make_binary_tensor_expression ( + std::forward(lhs), std::forward(rhs), std::minus{} + ); } -template -inline - constexpr auto operator*( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) + +template + requires ( + (boost::numeric::ublas::detail::TensorExpression) && + (boost::numeric::ublas::detail::TensorExpression) + ) +inline constexpr auto operator*( EL&& lhs, ER&& rhs ) noexcept { - static_assert( std::is_same_v< typename T1::value_type, typename T2::value_type>, + using ltensor_t = boost::numeric::ublas::detail::real_expression_type_t; + using rtensor_t = boost::numeric::ublas::detail::real_expression_type_t; + using lvalue_t = typename ltensor_t::value_type; + using rvalue_t = typename rtensor_t::value_type; + + static_assert( std::is_same_v< lvalue_t, rvalue_t >, "operator*() : LHS tensor and RHS tensor should have the same value type" ); - if constexpr( !std::is_same_v ){ - auto const& e = boost::numeric::ublas::detail::retrieve_extents(rhs); - - if( !boost::numeric::ublas::detail::all_extents_equal(lhs,e) ){ - throw std::runtime_error("operator+() : LHS tensor and RHS tensor should have equal extents"); - } - } - - return boost::numeric::ublas::detail::make_binary_tensor_expression (lhs(), rhs(), [](auto const& l, auto const& r){ return l * r; }); + return boost::numeric::ublas::detail::make_binary_tensor_expression ( + std::forward(lhs), std::forward(rhs), std::multiplies{} + ); } -template -inline - constexpr auto operator/( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) + +template + requires ( + (boost::numeric::ublas::detail::TensorExpression) && + (boost::numeric::ublas::detail::TensorExpression) + ) +inline constexpr auto operator/( EL&& lhs, ER&& rhs ) noexcept { - static_assert( std::is_same_v< typename T1::value_type, typename T2::value_type>, + using ltensor_t = boost::numeric::ublas::detail::real_expression_type_t; + using rtensor_t = boost::numeric::ublas::detail::real_expression_type_t; + using lvalue_t = typename ltensor_t::value_type; + using rvalue_t = typename rtensor_t::value_type; + + static_assert( std::is_same_v< lvalue_t, rvalue_t >, "operator/() : LHS tensor and RHS tensor should have the same value type" ); - if constexpr( !std::is_same_v ){ - auto e = boost::numeric::ublas::detail::retrieve_extents(rhs); - - if( !boost::numeric::ublas::detail::all_extents_equal(lhs,e) ){ - throw std::runtime_error("operator+() : LHS tensor and RHS tensor should have equal extents"); - } - } - - return boost::numeric::ublas::detail::make_binary_tensor_expression (lhs(), rhs(), std::divides<>{}); + return boost::numeric::ublas::detail::make_binary_tensor_expression ( + std::forward(lhs), std::forward(rhs), std::divides{} + ); } // Overloaded Arithmetic Operators with Scalars -template -inline - constexpr auto operator+(typename boost::numeric::ublas::tensor_core::const_reference lhs, - boost::numeric::ublas::detail::tensor_expression,R> const& rhs) noexcept -{ - using tensor_core_type = boost::numeric::ublas::tensor_core; - return boost::numeric::ublas::detail::make_unary_tensor_expression (rhs(), [lhs](auto const& r){ return lhs + r; }); -} -template -inline - constexpr auto operator-(typename boost::numeric::ublas::tensor_core::const_reference lhs, - boost::numeric::ublas::detail::tensor_expression,R> const& rhs) noexcept -{ - using tensor_core_type = boost::numeric::ublas::tensor_core; - return boost::numeric::ublas::detail::make_unary_tensor_expression (rhs(), [lhs](auto const& r){ return lhs - r; }); -} -template -inline - constexpr auto operator*(typename boost::numeric::ublas::tensor_core::const_reference lhs, - boost::numeric::ublas::detail::tensor_expression,R> const& rhs) noexcept -{ - using tensor_core_type = boost::numeric::ublas::tensor_core; - return boost::numeric::ublas::detail::make_unary_tensor_expression (rhs(), [lhs](auto const& r){ return lhs * r; }); -} -template -inline - constexpr auto operator/(typename boost::numeric::ublas::tensor_core::const_reference lhs, - boost::numeric::ublas::detail::tensor_expression,R> const& rhs) noexcept -{ - using tensor_core_type = boost::numeric::ublas::tensor_core; - return boost::numeric::ublas::detail::make_unary_tensor_expression (rhs(), [lhs](auto const& r){ return lhs / r; }); -} - - -template -inline - constexpr auto operator+(boost::numeric::ublas::detail::tensor_expression,L> const& lhs, - typename boost::numeric::ublas::tensor_core::const_reference rhs) noexcept -{ - using tensor_core_type = boost::numeric::ublas::tensor_core; - return boost::numeric::ublas::detail::make_unary_tensor_expression (lhs(), [rhs] (auto const& l) { return l + rhs; } ); -} -template -inline - constexpr auto operator-(boost::numeric::ublas::detail::tensor_expression,L> const& lhs, - typename boost::numeric::ublas::tensor_core::const_reference rhs) noexcept -{ - using tensor_core_type = boost::numeric::ublas::tensor_core; - return boost::numeric::ublas::detail::make_unary_tensor_expression (lhs(), [rhs] (auto const& l) { return l - rhs; } ); -} -template -inline - constexpr auto operator*(boost::numeric::ublas::detail::tensor_expression,L> const& lhs, - typename boost::numeric::ublas::tensor_core::const_reference rhs) noexcept -{ - using tensor_core_type = boost::numeric::ublas::tensor_core; - return boost::numeric::ublas::detail::make_unary_tensor_expression (lhs(), [rhs] (auto const& l) { return l * rhs; } ); -} -template -inline - constexpr auto operator/(boost::numeric::ublas::detail::tensor_expression,L> const& lhs, - typename boost::numeric::ublas::tensor_core::const_reference rhs) noexcept -{ - using tensor_core_type = boost::numeric::ublas::tensor_core; - return boost::numeric::ublas::detail::make_unary_tensor_expression (lhs(), [rhs] (auto const& l) { return l / rhs; } ); +template + requires (boost::numeric::ublas::detail::TensorExpression) +inline constexpr auto operator+( + typename boost::numeric::ublas::detail::real_expression_type_t::value_type lhs, + ER&& rhs +) noexcept { + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(rhs), + [lhs](value_type const& r){ return lhs + r; } + ); +} + +template + requires (boost::numeric::ublas::detail::TensorExpression) +inline constexpr auto operator-( + typename boost::numeric::ublas::detail::real_expression_type_t::value_type lhs, + ER&& rhs +) noexcept { + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(rhs), + [lhs](value_type const& r){ return lhs - r; } + ); +} + +template + requires (boost::numeric::ublas::detail::TensorExpression) +inline constexpr auto operator*( + typename boost::numeric::ublas::detail::real_expression_type_t::value_type lhs, + ER&& rhs +) noexcept { + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(rhs), + [lhs](value_type const& r){ return lhs * r; } + ); +} + +template + requires (boost::numeric::ublas::detail::TensorExpression) +inline constexpr auto operator/( + typename boost::numeric::ublas::detail::real_expression_type_t::value_type lhs, + ER&& rhs +) noexcept { + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(rhs), + [lhs](value_type const& r){ return lhs / r; } + ); +} + +template + requires (boost::numeric::ublas::detail::TensorExpression) +inline constexpr auto operator+( + EL&& lhs, + typename boost::numeric::ublas::detail::real_expression_type_t::value_type rhs +) noexcept { + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(lhs), + [rhs] (value_type const& l) { return l + rhs; } + ); +} + +template + requires (boost::numeric::ublas::detail::TensorExpression) +inline constexpr auto operator-( + EL&& lhs, + typename boost::numeric::ublas::detail::real_expression_type_t::value_type rhs +) noexcept { + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(lhs), + [rhs] (value_type const& l) { return l - rhs; } + ); +} + +template + requires (boost::numeric::ublas::detail::TensorExpression) +inline constexpr auto operator*( + EL&& lhs, + typename boost::numeric::ublas::detail::real_expression_type_t::value_type rhs +) noexcept { + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(lhs), + [rhs] (value_type const& l) { return l * rhs; } + ); +} + +template + requires (boost::numeric::ublas::detail::TensorExpression) +inline constexpr auto operator/( + EL&& lhs, + typename boost::numeric::ublas::detail::real_expression_type_t::value_type rhs +) noexcept { + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(lhs), + [rhs] (value_type const& l) { return l / rhs; } + ); } - - template -inline - constexpr auto& operator += (boost::numeric::ublas::tensor_core& lhs, - const boost::numeric::ublas::detail::tensor_expression,D> &expr) -{ - boost::numeric::ublas::detail::eval(lhs, expr(), [](auto& l, auto const& r) { l+=r; } ); +inline constexpr auto& operator += ( + boost::numeric::ublas::tensor_core& lhs, + boost::numeric::ublas::detail::tensor_expression,D> const& expr +){ + using value_type = typename boost::numeric::ublas::tensor_core::value_type; + boost::numeric::ublas::detail::eval(lhs, expr, [](value_type& l, value_type const& r) { l+=r; } ); return lhs; } template -inline - constexpr auto& operator -= (boost::numeric::ublas::tensor_core& lhs, - const boost::numeric::ublas::detail::tensor_expression,D> &expr) -{ - boost::numeric::ublas::detail::eval(lhs, expr(), [](auto& l, auto const& r) { l-=r; } ); +inline constexpr auto& operator -= ( + boost::numeric::ublas::tensor_core& lhs, + boost::numeric::ublas::detail::tensor_expression,D> const& expr +){ + using value_type = typename boost::numeric::ublas::tensor_core::value_type; + boost::numeric::ublas::detail::eval(lhs, expr, [](value_type& l, value_type const& r) { l-=r; } ); return lhs; } template -inline - constexpr auto& operator *= (boost::numeric::ublas::tensor_core& lhs, - const boost::numeric::ublas::detail::tensor_expression,D> &expr) -{ - boost::numeric::ublas::detail::eval(lhs, expr(), [](auto& l, auto const& r) { l*=r; } ); +inline constexpr auto& operator *= ( + boost::numeric::ublas::tensor_core& lhs, + boost::numeric::ublas::detail::tensor_expression,D> const& expr +){ + using value_type = typename boost::numeric::ublas::tensor_core::value_type; + boost::numeric::ublas::detail::eval(lhs, expr, [](value_type& l, value_type const& r) { l*=r; } ); return lhs; } template -inline - constexpr auto& operator /= (boost::numeric::ublas::tensor_core& lhs, - const boost::numeric::ublas::detail::tensor_expression,D> &expr) -{ - boost::numeric::ublas::detail::eval(lhs, expr(), [](auto& l, auto const& r) { l/=r; } ); +inline constexpr auto& operator /= ( + boost::numeric::ublas::tensor_core& lhs, + boost::numeric::ublas::detail::tensor_expression,D> const& expr +){ + using value_type = typename boost::numeric::ublas::tensor_core::value_type; + boost::numeric::ublas::detail::eval(lhs, expr, [](value_type& l, value_type const& r) { l/=r; } ); return lhs; } @@ -395,55 +449,62 @@ inline template -inline - constexpr auto& operator += (boost::numeric::ublas::tensor_core& lhs, - typename boost::numeric::ublas::tensor_core::const_reference r) -{ - boost::numeric::ublas::detail::eval(lhs, [r](auto& l) { l+=r; } ); +inline constexpr auto& operator += ( + boost::numeric::ublas::tensor_core& lhs, + typename boost::numeric::ublas::tensor_core::value_type r +) noexcept{ + using value_type = typename boost::numeric::ublas::tensor_core::value_type; + boost::numeric::ublas::detail::eval(lhs, [r](value_type& l) { l+=r; } ); return lhs; } -template -inline - constexpr auto& operator -= (boost::numeric::ublas::tensor_core& lhs, - typename boost::numeric::ublas::tensor_core::const_reference r) -{ - boost::numeric::ublas::detail::eval(lhs, [r](auto& l) { l-=r; } ); +template +inline constexpr auto& operator -= ( + boost::numeric::ublas::tensor_core& lhs, + typename boost::numeric::ublas::tensor_core::value_type r +) noexcept{ + using value_type = typename boost::numeric::ublas::tensor_core::value_type; + boost::numeric::ublas::detail::eval(lhs, [r](value_type& l) { l-=r; } ); return lhs; } -template -inline - constexpr auto& operator *= (boost::numeric::ublas::tensor_core& lhs, - typename boost::numeric::ublas::tensor_core::const_reference r) -{ - boost::numeric::ublas::detail::eval(lhs, [r](auto& l) { l*=r; } ); +template +inline constexpr auto& operator *= ( + boost::numeric::ublas::tensor_core& lhs, + typename boost::numeric::ublas::tensor_core::value_type r +) noexcept{ + using value_type = typename boost::numeric::ublas::tensor_core::value_type; + boost::numeric::ublas::detail::eval(lhs, [r](value_type& l) { l*=r; } ); return lhs; } -template -constexpr auto& operator /= (boost::numeric::ublas::tensor_core& lhs, - typename boost::numeric::ublas::tensor_core::const_reference r) -{ - boost::numeric::ublas::detail::eval(lhs, [r](auto& l) { l/=r; } ); - return lhs; +template +inline constexpr auto& operator /= ( + boost::numeric::ublas::tensor_core& lhs, + typename boost::numeric::ublas::tensor_core::value_type r +) noexcept{ + using value_type = typename boost::numeric::ublas::tensor_core::value_type; + boost::numeric::ublas::detail::eval(lhs, [r](value_type& l) { l/=r; } ); + return lhs; } - - - template inline constexpr - auto const& operator +(const boost::numeric::ublas::detail::tensor_expression& lhs) noexcept{ + auto const& operator +(boost::numeric::ublas::detail::tensor_expression const& lhs) noexcept{ return lhs; } -template -inline constexpr - auto operator -(boost::numeric::ublas::detail::tensor_expression const& lhs) { - return boost::numeric::ublas::detail::make_unary_tensor_expression (lhs(), std::negate<>{} ); +template + requires boost::numeric::ublas::detail::TensorExpression +inline constexpr auto operator -(E&& e) noexcept{ + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + using value_type = typename tensor_type::value_type; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(e), std::negate{} + ); } @@ -455,7 +516,7 @@ inline constexpr */ template -auto operator*( +constexpr auto operator*( std::pair< tensor_type_left const&, tuple_type_left > lhs, std::pair< tensor_type_right const&, tuple_type_right > rhs) { @@ -468,7 +529,7 @@ auto operator*( auto multi_index_left = lhs.second; auto multi_index_right = rhs.second; - static constexpr auto num_equal_ind = ublas::number_equal_indexes::value; + constexpr auto num_equal_ind = ublas::number_equal_indexes::value; if constexpr ( num_equal_ind == 0 ){ return tensor_left * tensor_right; diff --git a/include/boost/numeric/ublas/tensor/operators_comparison.hpp b/include/boost/numeric/ublas/tensor/operators_comparison.hpp index efc6c7323..179c54f4c 100644 --- a/include/boost/numeric/ublas/tensor/operators_comparison.hpp +++ b/include/boost/numeric/ublas/tensor/operators_comparison.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2018, Cem Bassoy, cem.bassoy@gmail.com +// Copyright (c) 2022, Amit Singh, amitsingh19975@gmail.com // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -25,79 +26,159 @@ namespace boost::numeric::ublas { template class tensor_core; + } // namespace boost::numeric::ublas namespace boost::numeric::ublas::detail { -template -[[nodiscard]] inline -constexpr bool compare(tensor_core const& lhs, tensor_core const& rhs, BinaryPred pred) +template +struct is_equality_functional_object + : std::false_type +{}; + +template +static constexpr bool is_equality_functional_object_v = is_equality_functional_object< std::decay_t >::value; + +template +struct is_equality_functional_object< std::equal_to > + : std::true_type +{}; + +template +struct is_equality_functional_object< std::not_equal_to > + : std::true_type +{}; + +template<> +struct is_equality_functional_object< std::equal_to<> > + : std::true_type +{}; + +template<> +struct is_equality_functional_object< std::not_equal_to<> > + : std::true_type +{}; + +template +[[nodiscard]] +constexpr auto compare_helper(LE const& le, RE const& re, std::true_type /*unused*/) noexcept + -> std::pair { - static_assert( std::is_same_v::value_type, typename tensor_core::value_type>, - "boost::numeric::ublas::detail::compare(tensor_core const&, tensor_core const&, BinaryPred) : " - "LHS and RHS both should have the same value type" - ); + using ::operator==; - if(::operator!=(lhs.extents(),rhs.extents())){ - if constexpr(!std::is_same>::value && !std::is_same>::value) + constexpr auto zero = SizeType{}; + + if constexpr( is_static_v< LE > && is_static_v< RE > ){ + constexpr bool is_same = std::is_same_v; + constexpr SizeType size = ( is_same ? SizeType{ product_v< LE > } : zero ); + return { is_same, size }; + }else{ + bool const is_same = ( le == re ); + SizeType const size = ( is_same ? SizeType{ product(le) } : zero ); + return { is_same, size }; + } +} + +template +[[nodiscard]] +constexpr auto compare_helper(LE const& le, RE const& re, std::false_type /*unused*/) + noexcept( is_static_v< LE> && is_static_v< RE > ) -> std::pair +{ + using ::operator!=; + + if constexpr( is_static_v< LE > && is_static_v< RE > ){ + static_assert(std::is_same_v< LE, RE >, + "boost::numeric::ublas::detail::compare_helper(Lextents const& lhs, Rextents const& rhs) : " + "cannot compare tensors with different shapes." + ); + + constexpr SizeType size = product_v< LE >; + return { true, size }; + }else{ + if(le != re){ throw std::runtime_error( - "boost::numeric::ublas::detail::compare(tensor_core const&, tensor_core const&, BinaryPred) : " + "boost::numeric::ublas::detail::compare_helper(Lextents const& lhs, Rextents const& rhs) : " "cannot compare tensors with different shapes." ); - else - return false; - } - - if constexpr(std::is_same>::value || std::is_same>::value) - if(lhs.empty()) - return false; + } - for(auto i = 0u; i < lhs.size(); ++i) - if(!pred(lhs(i), rhs(i))) - return false; - return true; + SizeType const size = product( le ); + return { true, size }; + } } -template +template [[nodiscard]] inline -constexpr bool compare(tensor_core const& rhs, UnaryPred pred) +constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) + noexcept( + ( is_static_v< std::decay_t< decltype(retrieve_extents(lhs)) > > && + is_static_v< std::decay_t< decltype(retrieve_extents(rhs)) > > + ) || is_equality_functional_object_v + ) { - for(auto i = 0u; i < rhs.size(); ++i) - if(!pred(rhs(i))) - return false; - return true; -} + auto const& lexpr = cast_tensor_expression(lhs); + auto const& rexpr = cast_tensor_expression(rhs); + using lvalue_type = decltype(lexpr(0)); + using rvalue_type = decltype(rexpr(0)); + + static_assert( same_exp< lvalue_type, rvalue_type >, + "boost::numeric::ublas::detail::compare(tensor_expresion const& lhs, tensor_expresion const& rhs, BinaryFn&& pred) : " + "both LHS and RHS should have the same value type" + ); -template -[[nodiscard]] -constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred pred) -{ - constexpr bool lhs_is_tensor = std::is_same::value; - constexpr bool rhs_is_tensor = std::is_same::value; + static_assert( + std::is_invocable_r_v, + "boost::numeric::ublas::detail::compare(tensor_expresion const& lhs, tensor_expresion const& rhs, BinaryFn&& pred) : " + "the predicate must be a binary predicate, and it must return a bool" + ); + + auto const& le = retrieve_extents(lexpr); + auto const& re = retrieve_extents(rexpr); + + using size_type = typename T1::size_type; + using is_eq_t = std::conditional_t< is_equality_functional_object_v, std::true_type, std::false_type >; + + auto const [status, size] = compare_helper(le, re, is_eq_t{}); - if constexpr (lhs_is_tensor && rhs_is_tensor) - return compare(static_cast( lhs ), static_cast( rhs ), pred); - else if constexpr (lhs_is_tensor && !rhs_is_tensor) - return compare(static_cast( lhs ), T2( rhs ), pred); - else if constexpr (!lhs_is_tensor && rhs_is_tensor) - return compare(T1( lhs ), static_cast( rhs ), pred); - else - return compare(T1( lhs ), T2( rhs ), pred); + for(auto i = size_type{}; i < size; ++i){ + if(!std::invoke(pred, lexpr(i), rexpr(i))) + return false; + } + return status; } + template [[nodiscard]] -constexpr bool compare(tensor_expression const& expr, UnaryPred pred) +constexpr bool compare(tensor_expression const& expr, UnaryPred&& pred) noexcept { - if constexpr (std::is_same::value) - return compare(static_cast( expr ), pred); - else - return compare(T( expr ), pred); + auto const& ue = cast_tensor_expression(expr); + auto const& e = retrieve_extents(ue); + + using size_type = typename T::size_type; + using value_type = decltype(ue(0)); + using extents_t = std::decay_t< decltype(e) >; + + static_assert( + std::is_invocable_r_v, + "boost::numeric::ublas::detail::compare(tensor_expresion const& expr, UnaryPred&& pred) : " + "the predicate must be an unary predicate, and it must return a bool" + ); + + size_type const size = is_static_v< extents_t > ? product_v< extents_t > : product( e ); + + for(auto i = size_type{}; i < size; ++i){ + if(!std::invoke(pred, ue(i))) + return false; + } + + return true; } + } // namespace boost::numeric::ublas::detail @@ -105,43 +186,72 @@ template [[nodiscard]] inline constexpr bool operator==( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( lhs, rhs, std::equal_to<>{} ); + boost::numeric::ublas::detail::tensor_expression const& rhs +) noexcept{ + using value_type = typename T1::value_type; + return boost::numeric::ublas::detail::compare( lhs, rhs, std::equal_to{} ); } + template [[nodiscard]] inline constexpr auto operator!=( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( lhs, rhs, std::not_equal_to<>{} ); + boost::numeric::ublas::detail::tensor_expression const& rhs +) noexcept{ + using value_type = typename T1::value_type; + return boost::numeric::ublas::detail::compare( lhs, rhs, std::not_equal_to{} ); } + template [[nodiscard]] inline constexpr auto operator< ( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( lhs, rhs, std::less<>{} ); + boost::numeric::ublas::detail::tensor_expression const& rhs +)noexcept( + boost::numeric::ublas::is_static_v< std::decay_t< decltype(boost::numeric::ublas::detail::retrieve_extents(lhs)) > > && + boost::numeric::ublas::is_static_v< std::decay_t< decltype(boost::numeric::ublas::detail::retrieve_extents(rhs)) > > +) { + using value_type = typename T1::value_type; + return boost::numeric::ublas::detail::compare( lhs, rhs, std::less{} ); } + template [[nodiscard]] inline constexpr auto operator<=( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( lhs, rhs, std::less_equal<>{} ); + boost::numeric::ublas::detail::tensor_expression const& rhs +)noexcept( + boost::numeric::ublas::is_static_v< std::decay_t< decltype(boost::numeric::ublas::detail::retrieve_extents(lhs)) > > && + boost::numeric::ublas::is_static_v< std::decay_t< decltype(boost::numeric::ublas::detail::retrieve_extents(rhs)) > > +) { + using value_type = typename T1::value_type; + return boost::numeric::ublas::detail::compare( lhs, rhs, std::less_equal{} ); } + template [[nodiscard]] inline constexpr auto operator> ( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater<>{} ); + boost::numeric::ublas::detail::tensor_expression const& rhs +)noexcept( + boost::numeric::ublas::is_static_v< std::decay_t< decltype(boost::numeric::ublas::detail::retrieve_extents(lhs)) > > && + boost::numeric::ublas::is_static_v< std::decay_t< decltype(boost::numeric::ublas::detail::retrieve_extents(rhs)) > > +) { + using value_type = typename T1::value_type; + return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater{} ); } + template [[nodiscard]] inline constexpr auto operator>=( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater_equal<>{} ); + boost::numeric::ublas::detail::tensor_expression const& rhs +)noexcept( + boost::numeric::ublas::is_static_v< std::decay_t< decltype(boost::numeric::ublas::detail::retrieve_extents(lhs)) > > && + boost::numeric::ublas::is_static_v< std::decay_t< decltype(boost::numeric::ublas::detail::retrieve_extents(rhs)) > > +) { + using value_type = typename T1::value_type; + return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater_equal{} ); } @@ -150,65 +260,78 @@ constexpr auto operator>=( template [[nodiscard]] inline -constexpr bool operator==( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs == r; } ); +constexpr bool operator==( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( rhs, [lhs](value_type const& r){ return lhs == r; } ); } template [[nodiscard]] inline -constexpr auto operator!=( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs != r; } ); +constexpr auto operator!=( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( rhs, [lhs](value_type const& r){ return lhs != r; } ); } template [[nodiscard]] inline -constexpr auto operator< ( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs < r; } ); +constexpr auto operator< ( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( rhs, [lhs](value_type const& r){ return lhs < r; } ); } template [[nodiscard]] inline -constexpr auto operator<=( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs <= r; } ); +constexpr auto operator<=( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( rhs, [lhs](value_type const& r){ return lhs <= r; } ); } template [[nodiscard]] inline -constexpr auto operator> ( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs > r; } ); +constexpr auto operator> ( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( rhs, [lhs](value_type const& r){ return lhs > r; } ); } template [[nodiscard]] inline -constexpr auto operator>=( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs >= r; } ); +constexpr auto operator>=( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( rhs, [lhs](value_type const& r){ return lhs >= r; } ); } template -bool operator==( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l == rhs; } ); +[[nodiscard]] inline +constexpr bool operator==( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( lhs, [rhs](value_type const& l){ return l == rhs; } ); } template [[nodiscard]] inline -constexpr auto operator!=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l != rhs; } ); +constexpr auto operator!=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( lhs, [rhs](value_type const& l){ return l != rhs; } ); } template [[nodiscard]] inline -constexpr auto operator< ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l < rhs; } ); +constexpr auto operator< ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( lhs, [rhs](value_type const& l){ return l < rhs; } ); } template [[nodiscard]] inline -constexpr auto operator<=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l <= rhs; } ); +constexpr auto operator<=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( lhs, [rhs](value_type const& l){ return l <= rhs; } ); } template [[nodiscard]] inline -constexpr auto operator> ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l > rhs; } ); +constexpr auto operator> ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( lhs, [rhs](value_type const& l){ return l > rhs; } ); } template [[nodiscard]] inline -constexpr auto operator>=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l >= rhs; } ); +constexpr auto operator>=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( lhs, [rhs](value_type const& l){ return l >= rhs; } ); } diff --git a/test/tensor/functions/test_functions_real_imag_conj.cpp b/test/tensor/functions/test_functions_real_imag_conj.cpp index 650bf18f7..52243afdd 100644 --- a/test/tensor/functions/test_functions_real_imag_conj.cpp +++ b/test/tensor/functions/test_functions_real_imag_conj.cpp @@ -47,7 +47,7 @@ BOOST_TEST_DECORATOR( ) BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_dynamic, TestTupleType, - boost::numeric::ublas::cpp_basic_std_types, + boost::numeric::ublas::cpp_basic_std_floating_types, boost::numeric::ublas::tuple_fixture_tensor_dynamic ) { @@ -72,15 +72,15 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_dynamic, auto r00 = complex_tensor_t(n); tensor_type b = (a+a) / value_type{2}; - tensor_type r1 = ublas::real( (a+a) / value_type{2} ); + tensor_type r1 = ublas::real( (a+a) / value_type{2} ); std::transform( b.begin(), b.end(), r0.begin(), [](auto const& l){ return std::real( l ); } ); BOOST_CHECK_EQUAL_COLLECTIONS(r0.begin(), r0.end(), r1.begin(), r1.end()); - tensor_type r2 = ublas::imag( (a+a) / value_type{2} ); + tensor_type r2 = ublas::imag( (a+a) / value_type{2} ); std::transform( b.begin(), b.end(), r0.begin(), [](auto const& l){ return std::imag( l ); } ); BOOST_CHECK_EQUAL_COLLECTIONS(r0.begin(), r0.end(), r2.begin(), r2.end()); - complex_tensor_t r3 = ublas::conj( (a+a) / value_type{2} ); + complex_tensor_t r3 = ublas::conj( (a+a) / value_type{2} ); std::transform( b.begin(), b.end(), r00.begin(), [](auto const& l){ return std::conj( l ); } ); BOOST_CHECK_EQUAL_COLLECTIONS(r00.begin(), r00.end(), r3.begin(), r3.end()); @@ -118,7 +118,7 @@ BOOST_TEST_DECORATOR( ) BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static_rank, TestTupleType, - boost::numeric::ublas::cpp_basic_std_types, + boost::numeric::ublas::cpp_basic_std_floating_types, boost::numeric::ublas::tuple_fixture_tensor_static_rank ) { @@ -194,7 +194,7 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static_rank, // ) // BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static, // TestTupleType, -// boost::numeric::ublas::cpp_basic_std_types, +// boost::numeric::ublas::cpp_basic_std_floating_types, // boost::numeric::ublas::tuple_fixture_tensor_static // ) // { diff --git a/test/tensor/tensor/test_tensor_assignment_operator.cpp b/test/tensor/tensor/test_tensor_assignment_operator.cpp index 2f630bfd4..83526cd00 100644 --- a/test/tensor/tensor/test_tensor_assignment_operator.cpp +++ b/test/tensor/tensor/test_tensor_assignment_operator.cpp @@ -37,41 +37,40 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_dynamic, using strides_t = typename tensor_t::strides_type; using fixture_type = boost::numeric::ublas::tuple_fixture_tensor_dynamic; - // FIXME: Enable this test after the expressions are fixed - // BOOST_TEST_CONTEXT("[Dynamic Tensor Assignment Operator] assigning tensor expression"){ + BOOST_TEST_CONTEXT("[Dynamic Tensor Assignment Operator] assigning tensor expression"){ - // auto const e = fixture_type::t32.extents(); + auto const e = fixture_type::t32.extents(); - // auto const a = tensor_t(e, value_type(1)); - // BOOST_REQUIRE_EQUAL ( a.size(), 6ul); - // BOOST_REQUIRE_EQUAL ( a.rank(), 2ul); - // BOOST_REQUIRE (!a.empty()); + auto const a = tensor_t(e, value_type(1)); + BOOST_REQUIRE_EQUAL ( a.size(), 6ul); + BOOST_REQUIRE_EQUAL ( a.rank(), 2ul); + BOOST_REQUIRE (!a.empty()); - // auto const three = value_type(3); - // auto const expr = a + three * a; + auto const three = value_type(3); + auto const expr = a + three * a; - // auto t = tensor_t(e); - // t = expr; - - // BOOST_REQUIRE_EQUAL ( t.size(), 6ul); - // BOOST_REQUIRE_EQUAL ( t.rank(), 2ul); - // BOOST_REQUIRE (!t.empty()); - // BOOST_CHECK_NO_THROW( (void)t.at(3ul)); - // BOOST_CHECK_THROW ( (void)t.at(0,1,1), std::invalid_argument); - // BOOST_CHECK_THROW ( (void)t.at(0,1,2), std::invalid_argument); - // BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); - // BOOST_CHECK_THROW ( (void)t.at(5,2), std::out_of_range); + auto t = tensor_t(e); + t = expr; + + BOOST_REQUIRE_EQUAL ( t.size(), 6ul); + BOOST_REQUIRE_EQUAL ( t.rank(), 2ul); + BOOST_REQUIRE (!t.empty()); + BOOST_CHECK_NO_THROW( (void)t.at(3ul)); + BOOST_CHECK_THROW ( (void)t.at(0,1,1), std::invalid_argument); + BOOST_CHECK_THROW ( (void)t.at(0,1,2), std::invalid_argument); + BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); + BOOST_CHECK_THROW ( (void)t.at(5,2), std::out_of_range); - // BOOST_REQUIRE ( t.extents() == e ); - // auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE ( t.extents() == e ); + auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Expression Assignment] testing tensor element(" << i << ") = " < ? strides_t{1, 3} : strides_t{2ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE ( t.extents() == e ); + auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Expression Assignment] testing tensor element(" << i << ") = " < ? strides_t{1, 3} : strides_t{2ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE ( t.extents() == shape_t{} ); + auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Expression Assignment] testing tensor element(" << i << ") = " <( t, uplus1 ); auto uexpr2 = ublas::detail::make_unary_tensor_expression( t, uplus2 ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr1.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr2.e) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr1.expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr2.expr()) >, tensor_t > ) ); for(auto i = 0ul; i < t.size(); ++i){ BOOST_CHECK_EQUAL( uexpr1(i), uplus1(t(i)) ); @@ -59,8 +59,8 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_dynamic, auto bexpr_uexpr = ublas::detail::make_binary_tensor_expression( uexpr1, uexpr2, bplus ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.el.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.er.e) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.left_expr().expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.right_expr().expr()) >, tensor_t > ) ); for(auto i = 0ul; i < t.size(); ++i){ @@ -69,10 +69,10 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_dynamic, auto bexpr_bexpr_uexpr = ublas::detail::make_binary_tensor_expression( bexpr_uexpr, t, bminus ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.el.el.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.el.er.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.er) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.er) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.left_expr().left_expr().expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.left_expr().right_expr().expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.right_expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.right_expr()) >, tensor_t > ) ); for(auto i = 0ul; i < t.size(); ++i){ BOOST_CHECK_EQUAL( bexpr_bexpr_uexpr(i), bminus(bexpr_uexpr(i),t(i)) ); @@ -113,8 +113,8 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static_rank, auto uexpr1 = ublas::detail::make_unary_tensor_expression( t, uplus1 ); auto uexpr2 = ublas::detail::make_unary_tensor_expression( t, uplus2 ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr1.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr2.e) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr1.expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr2.expr()) >, tensor_t > ) ); for(auto i = 0ul; i < t.size(); ++i){ BOOST_CHECK_EQUAL( uexpr1(i), uplus1(t(i)) ); @@ -126,8 +126,8 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static_rank, auto bexpr_uexpr = ublas::detail::make_binary_tensor_expression( uexpr1, uexpr2, bplus ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.el.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.er.e) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.left_expr().expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.right_expr().expr()) >, tensor_t > ) ); for(auto i = 0ul; i < t.size(); ++i){ @@ -136,10 +136,10 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static_rank, auto bexpr_bexpr_uexpr = ublas::detail::make_binary_tensor_expression( bexpr_uexpr, t, bminus ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.el.el.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.el.er.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.er) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.er) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.left_expr().left_expr().expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.left_expr().right_expr().expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.right_expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.right_expr()) >, tensor_t > ) ); for(auto i = 0ul; i < t.size(); ++i){ BOOST_CHECK_EQUAL( bexpr_bexpr_uexpr(i), bminus(bexpr_uexpr(i),t(i)) ); @@ -180,8 +180,8 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static, auto uexpr1 = ublas::detail::make_unary_tensor_expression( t, uplus1 ); auto uexpr2 = ublas::detail::make_unary_tensor_expression( t, uplus2 ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr1.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr2.e) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr1.expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr2.expr()) >, tensor_t > ) ); for(auto i = 0ul; i < t.size(); ++i){ BOOST_CHECK_EQUAL( uexpr1(i), uplus1(t(i)) ); @@ -193,8 +193,8 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static, auto bexpr_uexpr = ublas::detail::make_binary_tensor_expression( uexpr1, uexpr2, bplus ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.el.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.er.e) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.left_expr().expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_uexpr.right_expr().expr()) >, tensor_t > ) ); for(auto i = 0ul; i < t.size(); ++i){ @@ -203,10 +203,10 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static, auto bexpr_bexpr_uexpr = ublas::detail::make_binary_tensor_expression( bexpr_uexpr, t, bminus ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.el.el.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.el.er.e) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.er) >, tensor_t > ) ); - BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.er) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.left_expr().left_expr().expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.left_expr().right_expr().expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.right_expr()) >, tensor_t > ) ); + BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(bexpr_bexpr_uexpr.right_expr()) >, tensor_t > ) ); for(auto i = 0ul; i < t.size(); ++i){ BOOST_CHECK_EQUAL( bexpr_bexpr_uexpr(i), bminus(bexpr_uexpr(i),t(i)) ); diff --git a/test/tensor/tensor/test_tensor_construction.cpp b/test/tensor/tensor/test_tensor_construction.cpp index 59b794a06..d67aca891 100644 --- a/test/tensor/tensor/test_tensor_construction.cpp +++ b/test/tensor/tensor/test_tensor_construction.cpp @@ -148,39 +148,38 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_dynamic, // } - // FIXME: Enable this tests after the tensor expression is fixed - // BOOST_TEST_CONTEXT("[Dynamic Tensor Construction] construction using tensor expression"){ + BOOST_TEST_CONTEXT("[Dynamic Tensor Construction] construction using tensor expression"){ - // auto const e = fixture_type::t32.extents(); + auto const e = fixture_type::t32.extents(); - // auto const a = tensor_t(e, value_type(1)); - // BOOST_REQUIRE_EQUAL ( a.size(), 6ul); - // BOOST_REQUIRE_EQUAL ( a.rank(), 2ul); - // BOOST_REQUIRE (!a.empty()); + auto const a = tensor_t(e, value_type(1)); + BOOST_REQUIRE_EQUAL ( a.size(), 6ul); + BOOST_REQUIRE_EQUAL ( a.rank(), 2ul); + BOOST_REQUIRE (!a.empty()); - // auto const three = value_type(3); - // auto const expr = a + three * a; - // auto const t = tensor_t(expr); + auto const three = value_type(3); + auto const expr = a + three * a; + auto const t = tensor_t(expr); - // BOOST_REQUIRE_EQUAL ( t.size(), 6ul); - // BOOST_REQUIRE_EQUAL ( t.rank(), 2ul); - // BOOST_REQUIRE (!t.empty()); - // BOOST_CHECK_NO_THROW( (void)t.at(3ul)); - // BOOST_CHECK_THROW ( (void)t.at(0,1,1), std::invalid_argument); - // BOOST_CHECK_THROW ( (void)t.at(0,1,2), std::invalid_argument); - // BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); - // BOOST_CHECK_THROW ( (void)t.at(5,2), std::out_of_range); - - // BOOST_REQUIRE ( t.extents() == e ); - // auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE_EQUAL ( t.size(), 6ul); + BOOST_REQUIRE_EQUAL ( t.rank(), 2ul); + BOOST_REQUIRE (!t.empty()); + BOOST_CHECK_NO_THROW( (void)t.at(3ul)); + BOOST_CHECK_THROW ( (void)t.at(0,1,1), std::invalid_argument); + BOOST_CHECK_THROW ( (void)t.at(0,1,2), std::invalid_argument); + BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); + BOOST_CHECK_THROW ( (void)t.at(5,2), std::out_of_range); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Expression Constructor] Testing tensor element(" << i << ") = " < ? strides_t{1, 3} : strides_t{2ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + + auto const four = value_type(4); + for(auto i = 0ul; i < t.size(); ++i){ + BOOST_TEST_CHECKPOINT("[Tensor Expression Constructor] Testing tensor element(" << i << ") = " < ? strides_t{1, 3} : strides_t{2ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE ( t.extents() == e ); + auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Legacy uBLAS Matrix Expression Constructor] testing tensor element(" << i << ") = " < ? strides_t{1, 1} : strides_t{1ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE ( t.extents() == fixture_type::t21.extents() ); + auto const strides = (std::is_same_v ? strides_t{1, 1} : strides_t{1ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Legacy uBLAS Vector Expression Constructor] testing tensor element(" << i << ") = " <; - // using strides_t = typename tensor_t::strides_type; + using tensor_t = ublas::tensor_static_rank; + using strides_t = typename tensor_t::strides_type; - // auto const e = fixture_type::t32.extents(); + auto const e = fixture_type::t32.extents(); - // auto a = tensor_t(e); - // a = value_type(1); + auto a = tensor_t(e); + a = value_type(1); - // BOOST_REQUIRE_EQUAL ( a.size(), 6ul); - // BOOST_REQUIRE_EQUAL ( a.rank(), 2ul); - // BOOST_REQUIRE (!a.empty()); + BOOST_REQUIRE_EQUAL ( a.size(), 6ul); + BOOST_REQUIRE_EQUAL ( a.rank(), 2ul); + BOOST_REQUIRE (!a.empty()); - // auto const three = value_type(3); - // auto const expr = a + three * a; - // auto const t = tensor_t(expr); + auto const three = value_type(3); + auto const expr = a + three * a; + auto const t = tensor_t(expr); - // BOOST_REQUIRE_EQUAL ( t.size(), 6ul); - // BOOST_REQUIRE_EQUAL ( t.rank(), 2ul); - // BOOST_REQUIRE (!t.empty()); - // BOOST_CHECK_NO_THROW( (void)t.at(3ul)); - // BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); - // BOOST_CHECK_THROW ( (void)t.at(5,2), std::out_of_range); + BOOST_REQUIRE_EQUAL ( t.size(), 6ul); + BOOST_REQUIRE_EQUAL ( t.rank(), 2ul); + BOOST_REQUIRE (!t.empty()); + BOOST_CHECK_NO_THROW( (void)t.at(3ul)); + BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); + BOOST_CHECK_THROW ( (void)t.at(5,2), std::out_of_range); - // BOOST_REQUIRE ( t.extents() == e ); - // auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE ( t.extents() == e ); + auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Expression Constructor] Testing tensor element(" << i << ") = " <; - // using strides_t = typename tensor_t::strides_type; + BOOST_TEST_CONTEXT("[Static Rank Tensor Construction] construction using legacy ublas matrix expression"){ + using tensor_t = ublas::tensor_static_rank; + using strides_t = typename tensor_t::strides_type; - // using matrix_type = typename tensor_t::matrix_type; - // auto const e = fixture_type::t32.extents(); - // auto const a = matrix_type(e[0], e[1], value_type(1)); - // BOOST_REQUIRE_EQUAL ( a.size1(), e[0]); - // BOOST_REQUIRE_EQUAL ( a.size2(), e[1]); + using matrix_type = typename tensor_t::matrix_type; + auto const e = fixture_type::t32.extents(); + auto const a = matrix_type(e[0], e[1], value_type(1)); + BOOST_REQUIRE_EQUAL ( a.size1(), e[0]); + BOOST_REQUIRE_EQUAL ( a.size2(), e[1]); - // auto const three = value_type(3); - // auto const expr = a + three * a; + auto const three = value_type(3); + auto const expr = a + three * a; - // auto const t = tensor_t(expr); + auto const t = tensor_t(expr); - // BOOST_CHECK_NO_THROW( (void)t.at(3ul)); - // BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); - // BOOST_CHECK_THROW ( (void)t.at(5,2), std::out_of_range); + BOOST_CHECK_NO_THROW( (void)t.at(3ul)); + BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); + BOOST_CHECK_THROW ( (void)t.at(5,2), std::out_of_range); - // BOOST_REQUIRE ( t.extents() == e ); - // auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE ( t.extents() == e ); + auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Legacy uBLAS Matrix Expression Constructor] testing tensor element(" << i << ") = " <; - // using strides_t = typename tensor_t::strides_type; + BOOST_TEST_CONTEXT("[Static Rank Tensor Construction] construction using legacy ublas vector expression"){ + using tensor_t = ublas::tensor_static_rank; + using strides_t = typename tensor_t::strides_type; - // using vector_type = typename tensor_t::vector_type; - // auto const e = fixture_type::t2.extents(); - // auto const a = vector_type(e[0], value_type(1)); - // BOOST_REQUIRE_EQUAL ( a.size(), e[0]); + using vector_type = typename tensor_t::vector_type; + auto const e = fixture_type::t2.extents(); + auto const a = vector_type(e[0], value_type(1)); + BOOST_REQUIRE_EQUAL ( a.size(), e[0]); - // auto const three = value_type(3); - // auto const expr = a + three * a; - // auto const t = tensor_t(expr); + auto const three = value_type(3); + auto const expr = a + three * a; + auto const t = tensor_t(expr); - // BOOST_CHECK_NO_THROW( (void)t.at(1ul)); - // BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); + BOOST_CHECK_NO_THROW( (void)t.at(1ul)); + BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); - // BOOST_REQUIRE ( t.extents() == fixture_type::t21.extents() ); - // auto const strides = (std::is_same_v ? strides_t{1, 1} : strides_t{1ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE ( t.extents() == fixture_type::t21.extents() ); + auto const strides = (std::is_same_v ? strides_t{1, 1} : strides_t{1ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Legacy uBLAS Vector Expression Constructor] testing tensor element(" << i << ") = " <; @@ -771,41 +766,40 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static, // } - // FIXME: Enable this tests after the tensor expression is fixed - // BOOST_TEST_CONTEXT("[Static Tensor Construction] construction using tensor expression"){ + BOOST_TEST_CONTEXT("[Static Tensor Construction] construction using tensor expression"){ - // using tensor_t = typename fixture_type::t32_type; - // using shape_t = typename tensor_t::extents_type; - // using strides_t = typename tensor_t::strides_type; + using tensor_t = typename fixture_type::t32_type; + using shape_t = typename tensor_t::extents_type; + using strides_t = typename tensor_t::strides_type; - // auto a = tensor_t(value_type(1)); + auto a = tensor_t(value_type(1)); - // BOOST_REQUIRE_EQUAL ( a.size(), 6ul); - // BOOST_REQUIRE_EQUAL ( a.rank(), 2ul); - // BOOST_REQUIRE (!a.empty()); + BOOST_REQUIRE_EQUAL ( a.size(), 6ul); + BOOST_REQUIRE_EQUAL ( a.rank(), 2ul); + BOOST_REQUIRE (!a.empty()); - // auto const three = value_type(3); - // auto const expr = a + three * a; - // auto const t = tensor_t(expr); + auto const three = value_type(3); + auto const expr = a + three * a; + auto const t = tensor_t(expr); - // BOOST_REQUIRE_EQUAL ( t.size(), 6ul); - // BOOST_REQUIRE_EQUAL ( t.rank(), 2ul); - // BOOST_REQUIRE (!t.empty()); - // // TODO: Enable it after adding out of bound check to static tensor - // // BOOST_CHECK_NO_THROW( (void)t.at(3ul)); - // // BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); - // // BOOST_CHECK_THROW ( (void)t.at(5,2), std::out_of_range); + BOOST_REQUIRE_EQUAL ( t.size(), 6ul); + BOOST_REQUIRE_EQUAL ( t.rank(), 2ul); + BOOST_REQUIRE (!t.empty()); + // TODO: Enable it after adding out of bound check to static tensor + // BOOST_CHECK_NO_THROW( (void)t.at(3ul)); + // BOOST_CHECK_THROW ( (void)t.at(25ul), std::out_of_range); + // BOOST_CHECK_THROW ( (void)t.at(5,2), std::out_of_range); - // BOOST_REQUIRE ( t.extents() == shape_t{} ); - // auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE ( t.extents() == shape_t{} ); + auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Expression Constructor] Testing tensor element(" << i << ") = " < ? strides_t{1, 3} : strides_t{2ul, 1ul}); - // BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); + BOOST_REQUIRE ( t.extents() == shape_t{} ); + auto const strides = (std::is_same_v ? strides_t{1, 3} : strides_t{2ul, 1ul}); + BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(t.strides()), std::end(t.strides()), std::begin(strides), std::end(strides)); - // auto const four = value_type(4); - // for(auto i = 0ul; i < t.size(); ++i){ - // BOOST_TEST_CHECKPOINT("[Tensor Legacy uBLAS Matrix Expression Constructor] testing tensor element(" << i << ") = " <, tensor_t > ) ); - const auto & uexpr_uexpr_e_e = uexpr_uexpr.e.e; + const auto & uexpr_uexpr_e_e = uexpr_uexpr.expr().expr(); BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr_uexpr_e_e) >, tensor_t > ) ); } @@ -101,11 +101,11 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static_rank, BOOST_CHECK_EQUAL( uexpr_uexpr(i), uplus1(uplus1(t(i))) ); } - const auto & uexpr_e = uexpr.e; + const auto & uexpr_e = uexpr.expr(); BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr_e) >, tensor_t > ) ); - const auto & uexpr_uexpr_e_e = uexpr_uexpr.e.e; + const auto & uexpr_uexpr_e_e = uexpr_uexpr.expr().expr(); BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr_uexpr_e_e) >, tensor_t > ) ); } @@ -150,11 +150,11 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_static, BOOST_CHECK_EQUAL( uexpr_uexpr(i), uplus1(uplus1(t(i))) ); } - const auto & uexpr_e = uexpr.e; + const auto & uexpr_e = uexpr.expr(); BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr_e) >, tensor_t > ) ); - const auto & uexpr_uexpr_e_e = uexpr_uexpr.e.e; + const auto & uexpr_uexpr_e_e = uexpr_uexpr.expr().expr(); BOOST_CHECK( ( std::is_same_v< std::decay_t< decltype(uexpr_uexpr_e_e) >, tensor_t > ) ); } diff --git a/test/tensor/utility.hpp b/test/tensor/utility.hpp index 0c4e20a29..314560e6f 100644 --- a/test/tensor/utility.hpp +++ b/test/tensor/utility.hpp @@ -115,6 +115,10 @@ namespace boost::numeric::ublas{ // using cpp_std_types = zip>::with_t; using cpp_std_types = zip::with_t; + // CPP Standard: The effect of instantiating the template complex for any other type than float, double, and long double is unspecified. + // Implementations may forbid instantiation of such specializations. + using cpp_basic_std_floating_types = zip::with_t; + using cpp_basic_std_types = zip::with_t; using layout_test_types = std::tuple;