From 15c4476bdd4800577e0101e820a981d21efb9ce5 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Wed, 9 Feb 2022 19:48:21 +0530 Subject: [PATCH 01/18] fix(lifetime): extending the lifetime of temporary objects Fix for issue #125. The current implementation does not take into account the prvalue that creates an issue while storing it. The prvalue is destroyed after the end of its scope, and if you try access it, you are entering into undefined land of C++. To solve this issue, we employ the trait `std::is_lvalue_reference` to determine if need to extend the lifetime or not. If extension is needed, we store the value with the help of universal forwarding. Otherwise, we extend lifetime using `const &`. --- examples/tensor/simple_expressions.cpp | 42 ++ .../boost/numeric/ublas/tensor/expression.hpp | 215 ++++--- .../ublas/tensor/expression_evaluation.hpp | 106 ++-- .../ublas/tensor/operators_arithmetic.hpp | 597 ++++++++++-------- 4 files changed, 569 insertions(+), 391 deletions(-) diff --git a/examples/tensor/simple_expressions.cpp b/examples/tensor/simple_expressions.cpp index 81c6e1cf8..0d2c9942f 100644 --- a/examples/tensor/simple_expressions.cpp +++ b/examples/tensor/simple_expressions.cpp @@ -65,6 +65,48 @@ 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 + { + 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; + + 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; + + auto 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; + + 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; + + 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..d33c385a3 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,60 @@ 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{}; + +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 +86,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; @@ -40,11 +96,12 @@ struct tensor_expression inline constexpr auto const& operator()() const noexcept { return *static_cast (this); } - ~tensor_expression() = default; + constexpr tensor_expression(tensor_expression&&) noexcept = default; + constexpr tensor_expression& operator=(tensor_expression&&) noexcept = default; + constexpr ~tensor_expression() = default; + 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; protected : @@ -58,72 +115,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)) + {} + constexpr binary_tensor_expression(binary_tensor_expression&& l) noexcept = default; + constexpr binary_tensor_expression& operator=(binary_tensor_expression&& l) noexcept = default; + constexpr ~binary_tensor_expression() = default; + binary_tensor_expression(const binary_tensor_expression& l) = delete; binary_tensor_expression& operator=(binary_tensor_expression const& l) noexcept = delete; + [[nodiscard]] inline + constexpr decltype(auto) operator()(size_type i) const + requires (does_exp_need_cast_v && does_exp_need_cast_v) + { + return op(el()(i), er()(i)); + } + + [[nodiscard]] inline + constexpr decltype(auto) operator()(size_type i) const + requires (does_exp_need_cast_v && !does_exp_need_cast_v) + { + return op(el()(i), er(i)); + } + + [[nodiscard]] inline + constexpr decltype(auto) operator()(size_type i) const + requires (!does_exp_need_cast_v && does_exp_need_cast_v) + { + return op(el(i), er()(i)); + } [[nodiscard]] inline - constexpr decltype(auto) operator()(size_type i) const { return op(el(i), er(i)); } + constexpr decltype(auto) operator()(size_type i) const { + return op(el(i), er(i)); + } - expression_type_left const& el; - expression_type_right const& er; + 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 +193,53 @@ 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; + template Exp, typename OPType> + explicit constexpr unary_tensor_expression(Exp&& ee, OPType&& o) + : e(std::forward(ee)) + , op(std::forward(o)) + {} + constexpr unary_tensor_expression(unary_tensor_expression&& l) noexcept = default; + constexpr unary_tensor_expression& operator=(unary_tensor_expression&& l) noexcept = default; + constexpr ~unary_tensor_expression() = default; constexpr unary_tensor_expression() = delete; 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)); } + decltype(auto) operator()(size_type i) const + requires does_exp_need_cast_v + { + return op(e()(i)); + } + + [[nodiscard]] inline constexpr + decltype(auto) operator()(size_type i) const { + return op(e(i)); + } - E const& e; - OP op; + expression_type e; + unary_operation op; }; // \brief helper function to simply instantiation of lambda proxy class -template -[[nodiscard]] inline -constexpr auto make_unary_tensor_expression( tensor_expression const& 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 +template + requires ( MatrixExpression || VectorExpression || TensorExpression ) [[nodiscard]] inline -constexpr auto make_unary_tensor_expression( vector_expression const& e, OP op) noexcept +constexpr auto make_unary_tensor_expression( E&& 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..37e9f1e48 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 @@ -46,24 +46,38 @@ struct unary_tensor_expression; namespace boost::numeric::ublas::detail { template -struct has_tensor_types -{ static constexpr bool value = false; }; +struct has_tensor_types + : std::integral_constant< bool, same_exp > +{}; -template -struct has_tensor_types -{ static constexpr bool value = true; }; +template +static constexpr bool has_tensor_types_v = has_tensor_types< std::decay_t, std::decay_t >::value; template struct has_tensor_types> -{ static constexpr bool value = std::is_same::value || has_tensor_types::value; }; +{ + static constexpr bool value = + same_exp || + has_tensor_types >::value; +}; 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; }; +{ + static constexpr bool value = + same_exp || + same_exp || + has_tensor_types >::value || + has_tensor_types >::value; +}; template struct has_tensor_types> -{ static constexpr bool value = std::is_same::value || has_tensor_types::value; }; +{ + static constexpr bool value = + same_exp || + has_tensor_types >::value; +}; } // namespace boost::numeric::ublas::detail @@ -91,15 +105,15 @@ template [[nodiscard]] constexpr auto& retrieve_extents(tensor_expression const& expr) { - 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); + auto const& cast_expr = expr(); - if constexpr ( std::is_same::value ) + if constexpr ( same_exp ) return cast_expr.extents(); else - return retrieve_extents(cast_expr); + return retrieve_extents(cast_expr); } // Disable warning for unreachable code for MSVC compiler @@ -117,19 +131,19 @@ template [[nodiscard]] constexpr auto& retrieve_extents(binary_tensor_expression const& expr) { - 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 ) + if constexpr ( same_exp ) return expr.el.extents(); - if constexpr ( std::is_same::value ) + else if constexpr ( same_exp ) return expr.er.extents(); - else if constexpr ( detail::has_tensor_types::value ) + else if constexpr ( has_tensor_types_v ) return retrieve_extents(expr.el); - else if constexpr ( detail::has_tensor_types::value ) + else if constexpr ( has_tensor_types_v ) return retrieve_extents(expr.er); } @@ -148,13 +162,13 @@ template constexpr auto& retrieve_extents(unary_tensor_expression const& expr) { - 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 ) + if constexpr ( same_exp ) return expr.e.extents(); - else if constexpr ( detail::has_tensor_types::value ) + else if constexpr ( has_tensor_types_v ) return retrieve_extents(expr.e); } @@ -177,19 +191,19 @@ template constexpr auto all_extents_equal(tensor_expression const& expr, extents const& e) { - static_assert(detail::has_tensor_types>::value, - "Error in boost::numeric::ublas::detail::all_extents_equal: Expression to evaluate should contain tensors."); + static_assert(has_tensor_types_v>, + "Error in boost::numeric::ublas::all_extents_equal: Expression to evaluate should contain tensors."); - auto const& cast_expr = static_cast(expr); + auto const& cast_expr = expr(); - using ::operator==; - using ::operator!=; + using ::operator==; + using ::operator!=; - if constexpr ( std::is_same::value ) + if constexpr ( same_exp ) if( e != cast_expr.extents() ) return false; - if constexpr ( detail::has_tensor_types::value ) + if constexpr ( has_tensor_types_v ) if ( !all_extents_equal(cast_expr, e)) return false; @@ -201,25 +215,25 @@ template [[nodiscard]] constexpr auto all_extents_equal(binary_tensor_expression const& expr, extents const& e) { - static_assert(detail::has_tensor_types>::value, - "Error in boost::numeric::ublas::detail::all_extents_equal: Expression to evaluate should contain tensors."); + static_assert(has_tensor_types_v>, + "Error in boost::numeric::ublas::all_extents_equal: Expression to evaluate should contain tensors."); - using ::operator==; - using ::operator!=; + using ::operator==; + using ::operator!=; - if constexpr ( std::is_same::value ) + if constexpr ( same_exp ) if(e != expr.el.extents()) return false; - if constexpr ( std::is_same::value ) + if constexpr ( same_exp ) if(e != expr.er.extents()) return false; - if constexpr ( detail::has_tensor_types::value ) + if constexpr ( has_tensor_types_v ) if(!all_extents_equal(expr.el, e)) return false; - if constexpr ( detail::has_tensor_types::value ) + if constexpr ( has_tensor_types_v ) if(!all_extents_equal(expr.er, e)) return false; @@ -231,16 +245,16 @@ template [[nodiscard]] constexpr auto all_extents_equal(unary_tensor_expression const& expr, extents const& e) { - static_assert(detail::has_tensor_types>::value, - "Error in boost::numeric::ublas::detail::all_extents_equal: Expression to evaluate should contain tensors."); + static_assert(has_tensor_types_v>, + "Error in boost::numeric::ublas::all_extents_equal: Expression to evaluate should contain tensors."); - using ::operator==; + using ::operator==; - if constexpr ( std::is_same::value ) + if constexpr ( same_exp ) if(e != expr.e.extents()) return false; - if constexpr ( detail::has_tensor_types::value ) + if constexpr ( has_tensor_types_v ) if(!all_extents_equal(expr.e, e)) return false; @@ -263,8 +277,8 @@ namespace boost::numeric::ublas::detail template inline void eval(tensor_type& lhs, tensor_expression const& expr) { - if constexpr (detail::has_tensor_types >::value ) - if(!detail::all_extents_equal(expr, lhs.extents() )) + if constexpr (has_tensor_types_v > ) + if(!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 @@ -292,7 +306,7 @@ inline void eval(tensor_type& lhs, tensor_expression inline void eval(tensor_type& lhs, tensor_expression const& expr, unary_fn const fn) { - if constexpr (detail::has_tensor_types< tensor_type, tensor_expression >::value ) - if(!detail::all_extents_equal( expr, lhs.extents() )) + if constexpr (has_tensor_types_v< tensor_type, tensor_expression > ) + if(!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 diff --git a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp index fa89d431f..b6b6bf6db 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,184 +36,172 @@ 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; -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; -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; -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; -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; -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; -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; -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; -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 ) { - 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; + + static_assert( std::is_same_v< typename ltensor_t::value_type, typename rtensor_t::value_type>, "operator+() : LHS tensor and RHS tensor should have the same value type" ); - if constexpr( !std::is_same_v ){ + 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) ){ @@ -220,173 +209,242 @@ inline } } - 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), [](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 ) { - 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; + + static_assert( std::is_same_v< typename ltensor_t::value_type, typename rtensor_t::value_type>, "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 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"); + 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), [](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 ) { - 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; + + static_assert( std::is_same_v< typename ltensor_t::value_type, typename rtensor_t::value_type>, "operator*() : LHS tensor and RHS tensor should have the same value type" ); - if constexpr( !std::is_same_v ){ + 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"); + 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), [](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 ) { - 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; + + static_assert( std::is_same_v< typename ltensor_t::value_type, typename rtensor_t::value_type>, "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 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"); + 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), [](auto const& l, auto const& r){ return l / r; } + ); } // 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 + 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; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(rhs), + [lhs](auto 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; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(rhs), + [lhs](auto 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; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(rhs), + [lhs](auto 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; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(rhs), + [lhs](auto 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; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(lhs), + [rhs] (auto 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; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(lhs), + [rhs] (auto 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; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(lhs), + [rhs] (auto 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; + + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(lhs), + [rhs] (auto const& l) { return l / rhs; } + ); } -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 -inline - constexpr auto& operator += (boost::numeric::ublas::tensor_core& lhs, - const boost::numeric::ublas::detail::tensor_expression,D> &expr) -{ +inline constexpr auto& operator += ( + boost::numeric::ublas::tensor_core& lhs, + boost::numeric::ublas::detail::tensor_expression,D> const& expr +){ boost::numeric::ublas::detail::eval(lhs, expr(), [](auto& l, auto 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) -{ +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; } ); return lhs; } template -inline - constexpr auto& operator *= (boost::numeric::ublas::tensor_core& lhs, - const boost::numeric::ublas::detail::tensor_expression,D> &expr) -{ +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; } ); return lhs; } template -inline - constexpr auto& operator /= (boost::numeric::ublas::tensor_core& lhs, - const boost::numeric::ublas::detail::tensor_expression,D> &expr) -{ +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; } ); return lhs; } @@ -395,55 +453,56 @@ inline template -inline - constexpr auto& operator += (boost::numeric::ublas::tensor_core& lhs, - typename boost::numeric::ublas::tensor_core::const_reference r) -{ +inline constexpr auto& operator += ( + boost::numeric::ublas::tensor_core& lhs, + typename boost::numeric::ublas::tensor_core::value_type 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::const_reference r) -{ +template +inline constexpr auto& operator -= ( + boost::numeric::ublas::tensor_core& lhs, + typename boost::numeric::ublas::tensor_core::value_type 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::const_reference r) -{ +template +inline constexpr auto& operator *= ( + boost::numeric::ublas::tensor_core& lhs, + typename boost::numeric::ublas::tensor_core::value_type r +){ boost::numeric::ublas::detail::eval(lhs, [r](auto& l) { l*=r; } ); return lhs; } -template -constexpr auto& operator /= (boost::numeric::ublas::tensor_core& lhs, - typename boost::numeric::ublas::tensor_core::const_reference r) -{ +template +inline constexpr auto& operator /= ( + boost::numeric::ublas::tensor_core& lhs, + typename boost::numeric::ublas::tensor_core::value_type r +){ boost::numeric::ublas::detail::eval(lhs, [r](auto& l) { l/=r; } ); - return lhs; + 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) { + using tensor_type = boost::numeric::ublas::detail::real_expression_type_t; + return boost::numeric::ublas::detail::make_unary_tensor_expression ( + std::forward(e), std::negate<>{} + ); } From 8f5a5d1acacf1665f86cc2eedbc96d78421cdba7 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Thu, 10 Feb 2022 16:34:20 +0530 Subject: [PATCH 02/18] fix(comparision): use `same_exp` instead of `std::is_same` --- .../numeric/ublas/tensor/operators_comparison.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/operators_comparison.hpp b/include/boost/numeric/ublas/tensor/operators_comparison.hpp index efc6c7323..7db03d683 100644 --- a/include/boost/numeric/ublas/tensor/operators_comparison.hpp +++ b/include/boost/numeric/ublas/tensor/operators_comparison.hpp @@ -74,15 +74,15 @@ 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; + constexpr bool lhs_is_tensor = same_exp; + constexpr bool rhs_is_tensor = same_exp; if constexpr (lhs_is_tensor && rhs_is_tensor) - return compare(static_cast( lhs ), static_cast( rhs ), pred); + return compare(lhs(), rhs(), pred); else if constexpr (lhs_is_tensor && !rhs_is_tensor) - return compare(static_cast( lhs ), T2( rhs ), pred); + return compare(lhs(), T2( rhs ), pred); else if constexpr (!lhs_is_tensor && rhs_is_tensor) - return compare(T1( lhs ), static_cast( rhs ), pred); + return compare(T1( lhs ), rhs(), pred); else return compare(T1( lhs ), T2( rhs ), pred); @@ -92,8 +92,8 @@ template [[nodiscard]] constexpr bool compare(tensor_expression const& expr, UnaryPred pred) { - if constexpr (std::is_same::value) - return compare(static_cast( expr ), pred); + if constexpr (same_exp) + return compare(expr(), pred); else return compare(T( expr ), pred); } From 8178c6c52444908921c3b5dc969c24c7cf40a934 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Thu, 10 Feb 2022 17:45:03 +0530 Subject: [PATCH 03/18] refactor(comparision): decoupled `if constexpr` and cleaned-up compare. Decoupled each `constexpr if` conditions into function using requires clause, which cleaned-up the compare function. Also, added the check for static extents that removes the runtime check. --- examples/tensor/simple_expressions.cpp | 7 +- .../ublas/tensor/operators_comparison.hpp | 153 ++++++++++++------ 2 files changed, 110 insertions(+), 50 deletions(-) diff --git a/examples/tensor/simple_expressions.cpp b/examples/tensor/simple_expressions.cpp index 0d2c9942f..5b3cab68d 100644 --- a/examples/tensor/simple_expressions.cpp +++ b/examples/tensor/simple_expressions.cpp @@ -68,6 +68,7 @@ int main() // 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; @@ -76,6 +77,7 @@ int main() 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 @@ -83,7 +85,8 @@ int main() std::cout << "% --------------------------- " << std::endl << std::endl; std::cout << "E(6)=" << tensor(E_6) << ";" << std::endl << std::endl; - auto four = 4.f; + // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) + auto const four = 4.f; auto E_10 = E_6 + four; // formatted output @@ -91,6 +94,7 @@ int main() 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 @@ -98,6 +102,7 @@ int main() 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 diff --git a/include/boost/numeric/ublas/tensor/operators_comparison.hpp b/include/boost/numeric/ublas/tensor/operators_comparison.hpp index 7db03d683..afaa82439 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 @@ -32,70 +33,119 @@ namespace boost::numeric::ublas::detail template [[nodiscard]] inline -constexpr bool compare(tensor_core const& lhs, tensor_core const& rhs, BinaryPred pred) +constexpr bool compare(tensor_core const& lhs, tensor_core const& rhs, BinaryPred&& pred) noexcept + requires ( same_exp< BinaryPred, std::equal_to<> > || same_exp< BinaryPred, std::not_equal_to<> > ) { 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" ); - if(::operator!=(lhs.extents(),rhs.extents())){ - if constexpr(!std::is_same>::value && !std::is_same>::value) + using lex_t = typename tensor_core::extents_type; + using rex_t = typename tensor_core::extents_type; + + if constexpr(is_static_v && is_static_v){ + if constexpr(!same_exp) + return false; + }else{ + if(::operator!=(lhs.extents(),rhs.extents())){ + return false; + } + } + + return std::equal(lhs.begin(), lhs.end(), rhs.begin(), std::forward(pred)); +} + +template +[[nodiscard]] inline +constexpr bool compare(tensor_core const& lhs, tensor_core const& rhs, BinaryPred&& pred) + noexcept( + is_static_v::extents_type> && + is_static_v::extents_type> + ) +{ + 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 lex_t = typename tensor_core::extents_type; + using rex_t = typename tensor_core::extents_type; + + if constexpr(is_static_v && is_static_v){ + static_assert(same_exp, + "boost::numeric::ublas::detail::compare(tensor_core const&, tensor_core const&, BinaryPred) : " + "cannot compare tensors with different shapes." + ); + }else{ + if(::operator!=(lhs.extents(),rhs.extents())){ throw std::runtime_error( "boost::numeric::ublas::detail::compare(tensor_core const&, tensor_core const&, BinaryPred) : " "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; + return std::equal(lhs.begin(), lhs.end(), rhs.begin(), std::forward(pred)); } template [[nodiscard]] inline -constexpr bool compare(tensor_core const& rhs, UnaryPred pred) +constexpr bool compare(tensor_core const& rhs, UnaryPred&& pred) noexcept { - for(auto i = 0u; i < rhs.size(); ++i) - if(!pred(rhs(i))) - return false; - return true; + return std::all_of(rhs.begin(), rhs.end(), std::forward(pred)); } template [[nodiscard]] -constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred pred) +constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) noexcept + requires (same_exp && same_exp) { - constexpr bool lhs_is_tensor = same_exp; - constexpr bool rhs_is_tensor = same_exp; - - if constexpr (lhs_is_tensor && rhs_is_tensor) - return compare(lhs(), rhs(), pred); - else if constexpr (lhs_is_tensor && !rhs_is_tensor) - return compare(lhs(), T2( rhs ), pred); - else if constexpr (!lhs_is_tensor && rhs_is_tensor) - return compare(T1( lhs ), rhs(), pred); - else - return compare(T1( lhs ), T2( rhs ), pred); + return compare(lhs(), rhs(), std::forward(pred)); +} +template +[[nodiscard]] +constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) + requires (same_exp && !same_exp) +{ + auto const r = T2(rhs); // FIXME: why are we constructing a whole new tensor? + return compare(lhs(), r, std::forward(pred)); +} + +template +[[nodiscard]] +constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) + requires (!same_exp && same_exp) +{ + auto const l = T1(lhs); // FIXME: why are we constructing a whole new tensor? + return compare(l, rhs(), std::forward(pred)); +} + +template +[[nodiscard]] +constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) +{ + auto const l = T1(lhs); // FIXME: why are we constructing a whole new tensor? + auto const r = T2(rhs); // FIXME: why are we constructing a whole new tensor? + return compare(l, r, std::forward(pred)); } template [[nodiscard]] -constexpr bool compare(tensor_expression const& expr, UnaryPred pred) +constexpr bool compare(tensor_expression const& expr, UnaryPred&& pred) noexcept + requires same_exp { - if constexpr (same_exp) - return compare(expr(), pred); - else - return compare(T( expr ), pred); + return compare(expr(), std::forward(pred)); +} + +template +[[nodiscard]] +constexpr bool compare(tensor_expression const& expr, UnaryPred&& pred) +{ + auto const e = T(expr); // FIXME: why are we constructing a whole new tensor? + return compare(e, std::forward(pred)); } } // namespace boost::numeric::ublas::detail @@ -105,16 +155,18 @@ template [[nodiscard]] inline constexpr bool operator==( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { + boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ 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) { + boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ return boost::numeric::ublas::detail::compare( lhs, rhs, std::not_equal_to<>{} ); } + template [[nodiscard]] inline constexpr auto operator< ( @@ -122,6 +174,7 @@ constexpr auto operator< ( boost::numeric::ublas::detail::tensor_expression const& rhs) { return boost::numeric::ublas::detail::compare( lhs, rhs, std::less<>{} ); } + template [[nodiscard]] inline constexpr auto operator<=( @@ -129,6 +182,7 @@ constexpr auto operator<=( boost::numeric::ublas::detail::tensor_expression const& rhs) { return boost::numeric::ublas::detail::compare( lhs, rhs, std::less_equal<>{} ); } + template [[nodiscard]] inline constexpr auto operator> ( @@ -136,6 +190,7 @@ constexpr auto operator> ( boost::numeric::ublas::detail::tensor_expression const& rhs) { return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater<>{} ); } + template [[nodiscard]] inline constexpr auto operator>=( @@ -150,64 +205,64 @@ constexpr auto operator>=( template [[nodiscard]] inline -constexpr bool operator==( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { +constexpr bool operator==( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs == r; } ); } template [[nodiscard]] inline -constexpr auto operator!=( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { +constexpr auto operator!=( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs != r; } ); } template [[nodiscard]] inline -constexpr auto operator< ( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { +constexpr auto operator< ( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs < r; } ); } template [[nodiscard]] inline -constexpr auto operator<=( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { +constexpr auto operator<=( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs <= r; } ); } template [[nodiscard]] inline -constexpr auto operator> ( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { +constexpr auto operator> ( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs > r; } ); } template [[nodiscard]] inline -constexpr auto operator>=( typename T::const_reference lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { +constexpr auto operator>=( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs >= r; } ); } template -bool operator==( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { +constexpr bool operator==( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l == rhs; } ); } template [[nodiscard]] inline -constexpr auto operator!=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { +constexpr auto operator!=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l != rhs; } ); } template [[nodiscard]] inline -constexpr auto operator< ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { +constexpr auto operator< ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) { return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l < rhs; } ); } template [[nodiscard]] inline -constexpr auto operator<=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { +constexpr auto operator<=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) { return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l <= rhs; } ); } template [[nodiscard]] inline -constexpr auto operator> ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { +constexpr auto operator> ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) { return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l > rhs; } ); } template [[nodiscard]] inline -constexpr auto operator>=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::const_reference rhs) { +constexpr auto operator>=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) { return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l >= rhs; } ); } From 9b8c4e1d780b4309754ded6507dcb9019b973e4c Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Sat, 12 Feb 2022 19:11:03 +0530 Subject: [PATCH 04/18] test(enable): enable tests for expression templates --- .../test_tensor_assignment_operator.cpp | 165 ++++---- .../tensor/test_tensor_construction.cpp | 394 +++++++++--------- 2 files changed, 274 insertions(+), 285 deletions(-) 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 << ") = " < ? 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 +765,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 << ") = " < Date: Sat, 12 Feb 2022 19:38:55 +0530 Subject: [PATCH 05/18] fix(test): bind the prvalue (=3) to a variable Since the patch is fixing the lifetime of the tensor, we cannot combine a prvalue with the old ublas expression. Therefore, we have to bind the prvalue with the name to increase the lifetime. --- test/tensor/tensor/test_tensor_construction.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/tensor/tensor/test_tensor_construction.cpp b/test/tensor/tensor/test_tensor_construction.cpp index eb30d59c6..d67aca891 100644 --- a/test/tensor/tensor/test_tensor_construction.cpp +++ b/test/tensor/tensor/test_tensor_construction.cpp @@ -240,7 +240,8 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_dynamic, BOOST_REQUIRE_EQUAL ( a.size1(), e[0]); BOOST_REQUIRE_EQUAL ( a.size2(), e[1]); - auto const expr = a + value_type(3) * a; + auto const three = value_type(3); + auto const expr = a + three * a; auto const t = tensor_t(expr); From 8ec747c0c74d02a7f825a16531149101ed7b7074 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Sun, 13 Feb 2022 16:13:19 +0530 Subject: [PATCH 06/18] refactor(expression): add function cast_tensor_expression for casting This function casts any `tensor_expression` to its child class, and it also handles recursive casting to get the real expression that is stored inside the layers of `tensor_expression`. --- .../boost/numeric/ublas/tensor/expression.hpp | 69 ++++++++++--------- .../ublas/tensor/expression_evaluation.hpp | 50 +++++++++----- .../numeric/ublas/tensor/multiplication.hpp | 2 +- .../tensor/test_tensor_binary_expression.cpp | 48 ++++++------- .../tensor/test_tensor_unary_expression.cpp | 12 ++-- 5 files changed, 101 insertions(+), 80 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/expression.hpp b/include/boost/numeric/ublas/tensor/expression.hpp index d33c385a3..74ef08e1a 100644 --- a/include/boost/numeric/ublas/tensor/expression.hpp +++ b/include/boost/numeric/ublas/tensor/expression.hpp @@ -43,6 +43,33 @@ static constexpr bool does_exp_need_cast_v = does_exp_need_cast< std::decay_t 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 +constexpr auto const& cast_tensor_exression(tensor_expression const& e) noexcept{ + auto const& res = e(); + if constexpr(does_exp_need_cast_v) + return cast_tensor_exression(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 +constexpr auto const& cast_tensor_exression(E const& e) noexcept{ return e; } + template constexpr auto is_tensor_expression_impl(tensor_expression const*) -> std::true_type; @@ -137,33 +164,15 @@ struct binary_tensor_expression binary_tensor_expression(const binary_tensor_expression& l) = delete; binary_tensor_expression& operator=(binary_tensor_expression const& l) noexcept = delete; + constexpr auto const& left_expr() const noexcept{ return cast_tensor_exression(el); } + constexpr auto const& right_expr() const noexcept{ return cast_tensor_exression(er); } [[nodiscard]] inline - constexpr decltype(auto) operator()(size_type i) const - requires (does_exp_need_cast_v && does_exp_need_cast_v) - { - return op(el()(i), er()(i)); - } - - [[nodiscard]] inline - constexpr decltype(auto) operator()(size_type i) const - requires (does_exp_need_cast_v && !does_exp_need_cast_v) - { - return op(el()(i), er(i)); - } - - [[nodiscard]] inline - constexpr decltype(auto) operator()(size_type i) const - requires (!does_exp_need_cast_v && does_exp_need_cast_v) - { - return op(el(i), er()(i)); - } - - [[nodiscard]] inline - constexpr decltype(auto) operator()(size_type i) const { - return op(el(i), er(i)); + constexpr decltype(auto) operator()(size_type i) const { + return op(left_expr()(i), right_expr()(i)); } +private: expression_type_left el; expression_type_right er; binary_operation op; @@ -211,19 +220,15 @@ struct unary_tensor_expression constexpr unary_tensor_expression() = delete; unary_tensor_expression(unary_tensor_expression const& l) = delete; unary_tensor_expression& operator=(unary_tensor_expression const& l) noexcept = delete; - - [[nodiscard]] inline constexpr - decltype(auto) operator()(size_type i) const - requires does_exp_need_cast_v - { - return op(e()(i)); - } + + constexpr auto const& expr() const noexcept{ return cast_tensor_exression(e); } [[nodiscard]] inline constexpr - decltype(auto) operator()(size_type i) const { - return op(e(i)); + decltype(auto) operator()(size_type i) const { + return op(expr()(i)); } +private: expression_type e; unary_operation op; }; diff --git a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp index 37e9f1e48..b18203ce2 100644 --- a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp +++ b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp @@ -134,17 +134,20 @@ constexpr auto& retrieve_extents(binary_tensor_expression const& exp static_assert(has_tensor_types_v>, "Error in boost::numeric::ublas::detail::retrieve_extents: Expression to evaluate should contain tensors."); + auto const& lexpr = expr.left_expr(); + auto const& rexpr = expr.right_expr(); + if constexpr ( same_exp ) - return expr.el.extents(); + return lexpr.extents(); else if constexpr ( same_exp ) - return expr.er.extents(); + return rexpr.extents(); else if constexpr ( has_tensor_types_v ) - return retrieve_extents(expr.el); + return retrieve_extents(lexpr); else if constexpr ( has_tensor_types_v ) - return retrieve_extents(expr.er); + return retrieve_extents(rexpr); } #ifdef _MSC_VER @@ -164,12 +167,14 @@ constexpr auto& retrieve_extents(unary_tensor_expression const& expr) 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 ( same_exp ) - return expr.e.extents(); + return uexpr.extents(); else if constexpr ( has_tensor_types_v ) - return retrieve_extents(expr.e); + return retrieve_extents(uexpr); } } // namespace boost::numeric::ublas::detail @@ -221,20 +226,23 @@ constexpr auto all_extents_equal(binary_tensor_expression const& exp using ::operator==; using ::operator!=; + auto const& lexpr = expr.left_expr(); + auto const& rexpr = expr.right_expr(); + if constexpr ( same_exp ) - if(e != expr.el.extents()) + if(e != lexpr.extents()) return false; if constexpr ( same_exp ) - if(e != expr.er.extents()) + if(e != rexpr.extents()) return false; if constexpr ( has_tensor_types_v ) - if(!all_extents_equal(expr.el, e)) + if(!all_extents_equal(lexpr, e)) return false; if constexpr ( has_tensor_types_v ) - if(!all_extents_equal(expr.er, e)) + if(!all_extents_equal(rexpr, e)) return false; return true; @@ -250,12 +258,14 @@ constexpr auto all_extents_equal(unary_tensor_expression const& expr, ex using ::operator==; + auto const& uexpr = expr.expr(); + if constexpr ( same_exp ) - if(e != expr.e.extents()) + if(e != uexpr.extents()) return false; if constexpr ( has_tensor_types_v ) - if(!all_extents_equal(expr.e, e)) + if(!all_extents_equal(uexpr, e)) return false; return true; @@ -281,9 +291,11 @@ inline void eval(tensor_type& lhs, tensor_expression if(!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 + auto const& rhs = cast_tensor_exression(expr); + + #pragma omp parallel for for(auto i = 0u; i < lhs.size(); ++i) - lhs(i) = expr()(i); + lhs(i) = rhs(i); } /** @brief Evaluates expression for a tensor_core @@ -310,9 +322,11 @@ inline void eval(tensor_type& lhs, tensor_expression if(!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_exression(expr); + #pragma omp parallel for for(auto i = 0u; i < lhs.size(); ++i) - fn(lhs(i), expr()(i)); + fn(lhs(i), rhs(i)); } @@ -347,7 +363,7 @@ inline void eval(tensor_type& lhs, tensor_expression template inline void eval(tensor_type& lhs, unary_fn const& fn) { -#pragma omp parallel for + #pragma omp parallel for for(auto i = 0u; i < lhs.size(); ++i) 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/test/tensor/tensor/test_tensor_binary_expression.cpp b/test/tensor/tensor/test_tensor_binary_expression.cpp index 3bb1b436c..f03a61b0c 100644 --- a/test/tensor/tensor/test_tensor_binary_expression.cpp +++ b/test/tensor/tensor/test_tensor_binary_expression.cpp @@ -46,8 +46,8 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_dynamic, 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)) ); @@ -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_unary_expression.cpp b/test/tensor/tensor/test_tensor_unary_expression.cpp index 789f8de7d..1dcab3ac7 100644 --- a/test/tensor/tensor/test_tensor_unary_expression.cpp +++ b/test/tensor/tensor/test_tensor_unary_expression.cpp @@ -52,11 +52,11 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_dynamic, 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 > ) ); } @@ -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 > ) ); } From 7a936dddd28e3b54c0e8fe4bd94f8cede6bba9e3 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Sun, 13 Feb 2022 17:53:02 +0530 Subject: [PATCH 07/18] chore(expression): add `nodiscard` --- include/boost/numeric/ublas/tensor/expression.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/expression.hpp b/include/boost/numeric/ublas/tensor/expression.hpp index 74ef08e1a..d78631325 100644 --- a/include/boost/numeric/ublas/tensor/expression.hpp +++ b/include/boost/numeric/ublas/tensor/expression.hpp @@ -56,7 +56,7 @@ struct does_exp_need_cast< tensor_expression > : std::true_type{}; * @return child of tensor_expression that is not tensor_expression */ template -constexpr auto const& cast_tensor_exression(tensor_expression const& e) noexcept{ +[[nodiscard]] constexpr auto const& cast_tensor_exression(tensor_expression const& e) noexcept{ auto const& res = e(); if constexpr(does_exp_need_cast_v) return cast_tensor_exression(res); @@ -68,7 +68,7 @@ constexpr auto const& cast_tensor_exression(tensor_expression const& e) noe // 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 -constexpr auto const& cast_tensor_exression(E const& e) noexcept{ return e; } +[[nodiscard]] constexpr auto const& cast_tensor_exression(E const& e) noexcept{ return e; } template constexpr auto is_tensor_expression_impl(tensor_expression const*) -> std::true_type; @@ -164,8 +164,8 @@ struct binary_tensor_expression binary_tensor_expression(const binary_tensor_expression& l) = delete; binary_tensor_expression& operator=(binary_tensor_expression const& l) noexcept = delete; - constexpr auto const& left_expr() const noexcept{ return cast_tensor_exression(el); } - constexpr auto const& right_expr() const noexcept{ return cast_tensor_exression(er); } + [[nodiscard]] constexpr auto const& left_expr() const noexcept{ return cast_tensor_exression(el); } + [[nodiscard]] constexpr auto const& right_expr() const noexcept{ return cast_tensor_exression(er); } [[nodiscard]] inline constexpr decltype(auto) operator()(size_type i) const { @@ -221,7 +221,7 @@ struct unary_tensor_expression unary_tensor_expression(unary_tensor_expression const& l) = delete; unary_tensor_expression& operator=(unary_tensor_expression const& l) noexcept = delete; - constexpr auto const& expr() const noexcept{ return cast_tensor_exression(e); } + [[nodiscard]] constexpr auto const& expr() const noexcept{ return cast_tensor_exression(e); } [[nodiscard]] inline constexpr decltype(auto) operator()(size_type i) const { From b6a3dd83ab5c1f0d1ffd267c1f0965519b3e7f50 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Sun, 13 Feb 2022 22:30:34 +0530 Subject: [PATCH 08/18] refactor(comparision): remove the construction of a whole new tensor Previously, we would create a new tensor from the expression template every time there is a comparision. Also, fixed the spelling to `cast_tensor_expression` from `cast_tensor_exression`. --- .../boost/numeric/ublas/tensor/expression.hpp | 12 +- .../ublas/tensor/expression_evaluation.hpp | 8 +- .../ublas/tensor/operators_comparison.hpp | 196 ++++++++++-------- 3 files changed, 123 insertions(+), 93 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/expression.hpp b/include/boost/numeric/ublas/tensor/expression.hpp index d78631325..9d75b19af 100644 --- a/include/boost/numeric/ublas/tensor/expression.hpp +++ b/include/boost/numeric/ublas/tensor/expression.hpp @@ -56,10 +56,10 @@ struct does_exp_need_cast< tensor_expression > : std::true_type{}; * @return child of tensor_expression that is not tensor_expression */ template -[[nodiscard]] constexpr auto const& cast_tensor_exression(tensor_expression const& e) noexcept{ +[[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_exression(res); + return cast_tensor_expression(res); else return res; } @@ -68,7 +68,7 @@ template // 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_exression(E const& e) noexcept{ return e; } +[[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; @@ -164,8 +164,8 @@ struct binary_tensor_expression binary_tensor_expression(const binary_tensor_expression& l) = delete; binary_tensor_expression& operator=(binary_tensor_expression const& l) noexcept = delete; - [[nodiscard]] constexpr auto const& left_expr() const noexcept{ return cast_tensor_exression(el); } - [[nodiscard]] constexpr auto const& right_expr() const noexcept{ return cast_tensor_exression(er); } + [[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 { @@ -221,7 +221,7 @@ struct unary_tensor_expression unary_tensor_expression(unary_tensor_expression const& l) = delete; unary_tensor_expression& operator=(unary_tensor_expression const& l) noexcept = delete; - [[nodiscard]] constexpr auto const& expr() const noexcept{ return cast_tensor_exression(e); } + [[nodiscard]] constexpr auto const& expr() const noexcept{ return cast_tensor_expression(e); } [[nodiscard]] inline constexpr decltype(auto) operator()(size_type i) const { diff --git a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp index b18203ce2..e9b6560ec 100644 --- a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp +++ b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp @@ -291,8 +291,8 @@ inline void eval(tensor_type& lhs, tensor_expression if(!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_exression(expr); - + auto const& rhs = cast_tensor_expression(expr); + #pragma omp parallel for for(auto i = 0u; i < lhs.size(); ++i) lhs(i) = rhs(i); @@ -322,7 +322,7 @@ inline void eval(tensor_type& lhs, tensor_expression if(!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_exression(expr); + auto const& rhs = cast_tensor_expression(expr); #pragma omp parallel for for(auto i = 0u; i < lhs.size(); ++i) diff --git a/include/boost/numeric/ublas/tensor/operators_comparison.hpp b/include/boost/numeric/ublas/tensor/operators_comparison.hpp index afaa82439..08324608d 100644 --- a/include/boost/numeric/ublas/tensor/operators_comparison.hpp +++ b/include/boost/numeric/ublas/tensor/operators_comparison.hpp @@ -31,123 +31,153 @@ class tensor_core; namespace boost::numeric::ublas::detail { -template +template [[nodiscard]] inline -constexpr bool compare(tensor_core const& lhs, tensor_core const& rhs, BinaryPred&& pred) noexcept +constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) noexcept requires ( same_exp< BinaryPred, std::equal_to<> > || same_exp< BinaryPred, std::not_equal_to<> > ) { - 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 lvalue_type = decltype(lhs()(0)); + using rvalue_type = decltype(rhs()(0)); + + static_assert( same_exp< lvalue_type, rvalue_type >, + "boost::numeric::ublas::detail::compare : " + "both LHS and RHS should have the same value type" ); - using lex_t = typename tensor_core::extents_type; - using rex_t = typename tensor_core::extents_type; + auto const& le = retrieve_extents(lhs); + auto const& re = retrieve_extents(rhs); - if constexpr(is_static_v && is_static_v){ - if constexpr(!same_exp) - return false; - }else{ - if(::operator!=(lhs.extents(),rhs.extents())){ - return false; + using size_type = typename T1::size_type; + + // returns the pair containing false if extents are not equal + // else true, and the size of the container. + constexpr auto cal_size = [](auto const& le, auto const& re) + -> std::pair + { + using lex_t = std::decay_t< decltype(le) >; + using rex_t = std::decay_t< decltype(re) >; + + if constexpr(is_static_v< lex_t > && is_static_v< rex_t >){ + if constexpr(!same_exp< lex_t, rex_t >) + return { false, size_type{} }; + + return { true, product_v< lex_t > }; + } else { + if(::operator!=(le,re)) + return { false, size_type{} }; + + return { true, product( le ) }; } + }; + + auto const [status, size] = cal_size(le, re); + + for(auto i = size_type{}; i < size; ++i){ + auto const& lexpr = cast_tensor_expression(lhs); + auto const& rexpr = cast_tensor_expression(rhs); + + if(!std::invoke(pred, lexpr(i), rexpr(i))) + return false; } - return std::equal(lhs.begin(), lhs.end(), rhs.begin(), std::forward(pred)); + // return false if the status is false + return ( true & status ); } -template +template [[nodiscard]] inline -constexpr bool compare(tensor_core const& lhs, tensor_core const& rhs, BinaryPred&& pred) +constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) noexcept( - is_static_v::extents_type> && - is_static_v::extents_type> + is_static_v< std::decay_t< decltype(retrieve_extents(lhs)) > > && + is_static_v< std::decay_t< decltype(retrieve_extents(rhs)) > > ) { - 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 lvalue_type = decltype(lhs()(0)); + using rvalue_type = decltype(rhs()(0)); + + static_assert( same_exp< lvalue_type, rvalue_type >, + "boost::numeric::ublas::detail::compare : " + "both LHS and RHS should have the same value type" ); - using lex_t = typename tensor_core::extents_type; - using rex_t = typename tensor_core::extents_type; - - if constexpr(is_static_v && is_static_v){ - static_assert(same_exp, - "boost::numeric::ublas::detail::compare(tensor_core const&, tensor_core const&, BinaryPred) : " - "cannot compare tensors with different shapes." - ); - }else{ - if(::operator!=(lhs.extents(),rhs.extents())){ - throw std::runtime_error( - "boost::numeric::ublas::detail::compare(tensor_core const&, tensor_core const&, BinaryPred) : " + auto const& le = retrieve_extents(lhs); + auto const& re = retrieve_extents(rhs); + + using size_type = typename T1::size_type; + + // returns the size of the container + constexpr auto cal_size = [](auto const& le, auto const& re) + -> size_type + { + using lex_t = std::decay_t< decltype(le) >; + using rex_t = std::decay_t< decltype(re) >; + + if constexpr(is_static_v< lex_t > && is_static_v< rex_t >){ + static_assert(same_exp< lex_t, rex_t >, + "boost::numeric::ublas::detail::compare : " "cannot compare tensors with different shapes." ); - } - } - return std::equal(lhs.begin(), lhs.end(), rhs.begin(), std::forward(pred)); -} + return product_v< lex_t >; + }else{ + if(::operator!=(le,re)){ + throw std::runtime_error( + "boost::numeric::ublas::detail::compare : " + "cannot compare tensors with different shapes." + ); + } -template -[[nodiscard]] inline -constexpr bool compare(tensor_core const& rhs, UnaryPred&& pred) noexcept -{ - return std::all_of(rhs.begin(), rhs.end(), std::forward(pred)); -} + return product( le ); + } + }; + size_type const size = cal_size(le, re); -template -[[nodiscard]] -constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) noexcept - requires (same_exp && same_exp) -{ - return compare(lhs(), rhs(), std::forward(pred)); -} + for(auto i = size_type{}; i < size; ++i){ + auto const& lexpr = cast_tensor_expression(lhs); + auto const& rexpr = cast_tensor_expression(rhs); + + if(!std::invoke(pred, lexpr(i), rexpr(i))) + return false; + } -template -[[nodiscard]] -constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) - requires (same_exp && !same_exp) -{ - auto const r = T2(rhs); // FIXME: why are we constructing a whole new tensor? - return compare(lhs(), r, std::forward(pred)); + return true; } -template -[[nodiscard]] -constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) - requires (!same_exp && same_exp) -{ - auto const l = T1(lhs); // FIXME: why are we constructing a whole new tensor? - return compare(l, rhs(), std::forward(pred)); -} - -template -[[nodiscard]] -constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) -{ - auto const l = T1(lhs); // FIXME: why are we constructing a whole new tensor? - auto const r = T2(rhs); // FIXME: why are we constructing a whole new tensor? - return compare(l, r, std::forward(pred)); -} template [[nodiscard]] constexpr bool compare(tensor_expression const& expr, UnaryPred&& pred) noexcept - requires same_exp { - return compare(expr(), std::forward(pred)); -} + auto const& e = retrieve_extents(expr); -template -[[nodiscard]] -constexpr bool compare(tensor_expression const& expr, UnaryPred&& pred) -{ - auto const e = T(expr); // FIXME: why are we constructing a whole new tensor? - return compare(e, std::forward(pred)); + using size_type = typename T::size_type; + + // returns the size of the container + constexpr auto cal_size = [](auto const& e) + -> size_type + { + using extents_t = std::decay_t< decltype(e) >; + + if constexpr(is_static_v< extents_t >) + return product_v< extents_t >; + else + return product( e ); + }; + + size_type const size = cal_size(e); + + for(auto i = size_type{}; i < size; ++i){ + auto const& ue = cast_tensor_expression(expr); + + if(!std::invoke(pred, ue(i))) + return false; + } + + return true; } + } // namespace boost::numeric::ublas::detail From 3a013d0c6f607b3adf8938892d8e85937a513947 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Sun, 13 Feb 2022 23:24:19 +0530 Subject: [PATCH 09/18] fix(compare): get the value type after `cast_tensor_expression` --- .../ublas/tensor/operators_comparison.hpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/operators_comparison.hpp b/include/boost/numeric/ublas/tensor/operators_comparison.hpp index 08324608d..c280bf418 100644 --- a/include/boost/numeric/ublas/tensor/operators_comparison.hpp +++ b/include/boost/numeric/ublas/tensor/operators_comparison.hpp @@ -36,16 +36,20 @@ template constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) noexcept requires ( same_exp< BinaryPred, std::equal_to<> > || same_exp< BinaryPred, std::not_equal_to<> > ) { - using lvalue_type = decltype(lhs()(0)); - using rvalue_type = decltype(rhs()(0)); + + 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 : " "both LHS and RHS should have the same value type" ); - auto const& le = retrieve_extents(lhs); - auto const& re = retrieve_extents(rhs); + auto const& le = retrieve_extents(lexpr); + auto const& re = retrieve_extents(rexpr); using size_type = typename T1::size_type; @@ -73,9 +77,6 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression const& lhs, tensor_expression > ) { - using lvalue_type = decltype(lhs()(0)); - using rvalue_type = decltype(rhs()(0)); + 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 : " "both LHS and RHS should have the same value type" ); - auto const& le = retrieve_extents(lhs); - auto const& re = retrieve_extents(rhs); + auto const& le = retrieve_extents(lexpr); + auto const& re = retrieve_extents(rexpr); using size_type = typename T1::size_type; @@ -134,9 +138,6 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression [[nodiscard]] constexpr bool compare(tensor_expression const& expr, UnaryPred&& pred) noexcept { - auto const& e = retrieve_extents(expr); + auto const& ue = cast_tensor_expression(expr); + auto const& e = retrieve_extents(ue); using size_type = typename T::size_type; @@ -168,8 +170,6 @@ constexpr bool compare(tensor_expression const& expr, UnaryPred&& pred) noe size_type const size = cal_size(e); for(auto i = size_type{}; i < size; ++i){ - auto const& ue = cast_tensor_expression(expr); - if(!std::invoke(pred, ue(i))) return false; } From 7b2309271d013baa8689418a24d9db97c11bb3c7 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Mon, 14 Feb 2022 14:12:00 +0530 Subject: [PATCH 10/18] refactor(expression): validate predicate and add noexcept --- .../ublas/tensor/operators_arithmetic.hpp | 32 ++++++++-- .../ublas/tensor/operators_comparison.hpp | 58 +++++++++++++++---- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp index b6b6bf6db..4328b1c07 100644 --- a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp +++ b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp @@ -191,7 +191,13 @@ template (boost::numeric::ublas::detail::TensorExpression) && (boost::numeric::ublas::detail::TensorExpression) ) -inline constexpr auto operator+( EL&& lhs, ER&& rhs ) +inline constexpr auto operator+( EL&& lhs, ER&& rhs ) + noexcept( + std::is_same_v< + boost::numeric::ublas::detail::real_expression_type_t, + boost::numeric::ublas::detail::real_expression_type_t + > + ) { using ltensor_t = boost::numeric::ublas::detail::real_expression_type_t; @@ -220,7 +226,13 @@ template (boost::numeric::ublas::detail::TensorExpression) && (boost::numeric::ublas::detail::TensorExpression) ) -inline constexpr auto operator-( EL&& lhs, ER&& rhs ) +inline constexpr auto operator-( EL&& lhs, ER&& rhs ) + noexcept( + std::is_same_v< + boost::numeric::ublas::detail::real_expression_type_t, + boost::numeric::ublas::detail::real_expression_type_t + > + ) { using ltensor_t = boost::numeric::ublas::detail::real_expression_type_t; @@ -248,7 +260,13 @@ template (boost::numeric::ublas::detail::TensorExpression) && (boost::numeric::ublas::detail::TensorExpression) ) -inline constexpr auto operator*( EL&& lhs, ER&& rhs ) +inline constexpr auto operator*( EL&& lhs, ER&& rhs ) + noexcept( + std::is_same_v< + boost::numeric::ublas::detail::real_expression_type_t, + boost::numeric::ublas::detail::real_expression_type_t + > + ) { using ltensor_t = boost::numeric::ublas::detail::real_expression_type_t; @@ -276,7 +294,13 @@ template (boost::numeric::ublas::detail::TensorExpression) && (boost::numeric::ublas::detail::TensorExpression) ) -inline constexpr auto operator/( EL&& lhs, ER&& rhs ) +inline constexpr auto operator/( EL&& lhs, ER&& rhs ) + noexcept( + std::is_same_v< + boost::numeric::ublas::detail::real_expression_type_t, + boost::numeric::ublas::detail::real_expression_type_t + > + ) { using ltensor_t = boost::numeric::ublas::detail::real_expression_type_t; diff --git a/include/boost/numeric/ublas/tensor/operators_comparison.hpp b/include/boost/numeric/ublas/tensor/operators_comparison.hpp index c280bf418..fa91ee29c 100644 --- a/include/boost/numeric/ublas/tensor/operators_comparison.hpp +++ b/include/boost/numeric/ublas/tensor/operators_comparison.hpp @@ -48,6 +48,12 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression, + "boost::numeric::ublas::detail::compare(lhs,rhs,pred) :" + "predicate must be a binary predicate, and it must return a bool" + ); + auto const& le = retrieve_extents(lexpr); auto const& re = retrieve_extents(rexpr); @@ -104,6 +110,12 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression, + "boost::numeric::ublas::detail::compare(lhs,rhs,pred) :" + "predicate must be a binary predicate, and it must return a bool" + ); + auto const& le = retrieve_extents(lexpr); auto const& re = retrieve_extents(rexpr); @@ -154,6 +166,12 @@ constexpr bool compare(tensor_expression const& expr, UnaryPred&& pred) noe auto const& e = retrieve_extents(ue); using size_type = typename T::size_type; + + static_assert( + std::is_invocable_r_v, + "boost::numeric::ublas::detail::compare(expr,pred) :" + "predicate must be an unary predicate, and it must return a bool" + ); // returns the size of the container constexpr auto cal_size = [](auto const& e) @@ -201,7 +219,11 @@ template [[nodiscard]] inline constexpr auto operator< ( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { + 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)) > > +) { return boost::numeric::ublas::detail::compare( lhs, rhs, std::less<>{} ); } @@ -209,7 +231,11 @@ template [[nodiscard]] inline constexpr auto operator<=( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { + 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)) > > +) { return boost::numeric::ublas::detail::compare( lhs, rhs, std::less_equal<>{} ); } @@ -217,7 +243,11 @@ template [[nodiscard]] inline constexpr auto operator> ( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { + 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)) > > +) { return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater<>{} ); } @@ -225,7 +255,11 @@ template [[nodiscard]] inline constexpr auto operator>=( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) { + 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)) > > +) { return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater_equal<>{} ); } @@ -245,22 +279,22 @@ constexpr auto operator!=( typename T::value_type lhs, boost::numeric::ublas::de } template [[nodiscard]] inline -constexpr auto operator< ( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { +constexpr auto operator< ( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs < r; } ); } template [[nodiscard]] inline -constexpr auto operator<=( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { +constexpr auto operator<=( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs <= r; } ); } template [[nodiscard]] inline -constexpr auto operator> ( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { +constexpr auto operator> ( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs > r; } ); } template [[nodiscard]] inline -constexpr auto operator>=( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) { +constexpr auto operator>=( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs >= r; } ); } @@ -277,22 +311,22 @@ constexpr auto operator!=( boost::numeric::ublas::detail::tensor_expression } template [[nodiscard]] inline -constexpr auto operator< ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) { +constexpr auto operator< ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l < rhs; } ); } template [[nodiscard]] inline -constexpr auto operator<=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) { +constexpr auto operator<=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l <= rhs; } ); } template [[nodiscard]] inline -constexpr auto operator> ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) { +constexpr auto operator> ( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l > rhs; } ); } template [[nodiscard]] inline -constexpr auto operator>=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) { +constexpr auto operator>=( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l >= rhs; } ); } From a90573c184c48aa010a68420a3049758b4e3b25a Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Mon, 14 Feb 2022 18:07:33 +0530 Subject: [PATCH 11/18] refactor(eval): clean the implementation --- .../ublas/tensor/expression_evaluation.hpp | 96 ++++++++----------- 1 file changed, 41 insertions(+), 55 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp index e9b6560ec..fcc357529 100644 --- a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp +++ b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp @@ -277,78 +277,63 @@ 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) { - if constexpr (has_tensor_types_v > ) - if(!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) = rhs(i); -} - -/** @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" + using rtensor_t = typename std::decay_t::tensor_type; + using ltensor_t = tensor_core; + using lvalue_type = typename ltensor_t::value_type; + using rvalue_type = typename rtensor_t::value_type; + using lextents_t = typename ltensor_t::extents_type; + using rextents_t = typename rtensor_t::extents_type; + + static_assert(std::is_same_v, + "boost::numeric::ublas::detail::eval(tensor_core&, TensorExpression auto const&) : " + "both LHS and RHS tensors should have same value type" ); - if ( !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::tensor_core: " + "both LHS and RHS tensors should have same shape." + ); + }else{ + if ( !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) = rhs(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) { - - if constexpr (has_tensor_types_v< tensor_type, tensor_expression > ) - if(!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) - fn(lhs(i), rhs(i)); + eval(lhs, expr, [](auto& l, auto const& r){ + l = r; + }); } @@ -360,12 +345,13 @@ 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) { #pragma omp parallel for for(auto i = 0u; i < lhs.size(); ++i) - fn(lhs(i)); + std::invoke(fn, lhs(i)); } From 2abe215964ec39dabf0d9b75a0d3a15e9a57833c Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Tue, 15 Feb 2022 14:52:35 +0530 Subject: [PATCH 12/18] fix(expression): disable the move constructor for public use to avoid undefined behaviour --- .../boost/numeric/ublas/tensor/expression.hpp | 73 +++++++++++++++---- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/expression.hpp b/include/boost/numeric/ublas/tensor/expression.hpp index 9d75b19af..ad72157a3 100644 --- a/include/boost/numeric/ublas/tensor/expression.hpp +++ b/include/boost/numeric/ublas/tensor/expression.hpp @@ -123,16 +123,30 @@ struct tensor_expression inline constexpr auto const& operator()() const noexcept { return *static_cast (this); } - constexpr tensor_expression(tensor_expression&&) noexcept = default; - constexpr tensor_expression& operator=(tensor_expression&&) noexcept = default; constexpr ~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& operator=(const tensor_expression&) = delete; - + constexpr tensor_expression& operator=(tensor_expression&&) noexcept = delete; 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 the only way to access the protected move constructor of other expressions. + template friend struct tensor_expression; }; @@ -157,20 +171,36 @@ struct binary_tensor_expression , er(std::forward(r)) , op(std::forward(o)) {} - constexpr binary_tensor_expression(binary_tensor_expression&& l) noexcept = default; - constexpr binary_tensor_expression& operator=(binary_tensor_expression&& l) noexcept = default; constexpr ~binary_tensor_expression() = default; + /// @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; [[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 op(left_expr()(i), right_expr()(i)); - } + constexpr decltype(auto) operator()(size_type i) const { return 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 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_left el; @@ -213,20 +243,37 @@ struct unary_tensor_expression : e(std::forward(ee)) , op(std::forward(o)) {} - constexpr unary_tensor_expression(unary_tensor_expression&& l) noexcept = default; - constexpr unary_tensor_expression& operator=(unary_tensor_expression&& l) noexcept = default; constexpr ~unary_tensor_expression() = default; constexpr unary_tensor_expression() = delete; + + /// @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; + constexpr unary_tensor_expression& operator=(unary_tensor_expression&& l) noexcept = delete; [[nodiscard]] constexpr auto const& expr() const noexcept{ return cast_tensor_expression(e); } [[nodiscard]] inline constexpr - decltype(auto) operator()(size_type i) const { - return op(expr()(i)); - } + decltype(auto) operator()(size_type i) const { return 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 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; From 51674d50ee5294150be7acd23ec408781e81cae1 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Tue, 15 Feb 2022 18:50:04 +0530 Subject: [PATCH 13/18] refactor(expression): clean unnecessary code and fix the comparision between different extents types --- .../boost/numeric/ublas/tensor/expression.hpp | 6 +- .../ublas/tensor/expression_evaluation.hpp | 165 ++++++++---------- .../ublas/tensor/operators_arithmetic.hpp | 80 ++------- 3 files changed, 87 insertions(+), 164 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/expression.hpp b/include/boost/numeric/ublas/tensor/expression.hpp index ad72157a3..6e70699b9 100644 --- a/include/boost/numeric/ublas/tensor/expression.hpp +++ b/include/boost/numeric/ublas/tensor/expression.hpp @@ -145,7 +145,7 @@ protected : constexpr tensor_expression(tensor_expression&&) noexcept = default; explicit tensor_expression() = default; - /// @brief This the only way to access the protected move constructor of other expressions. + /// @brief This is the only way to access the protected move constructor of other expressions. template friend struct tensor_expression; }; @@ -198,7 +198,7 @@ struct binary_tensor_expression */ constexpr binary_tensor_expression(binary_tensor_expression&& l) noexcept = default; - /// @brief This the only way to access the protected move constructor of other expressions. + /// @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; @@ -271,7 +271,7 @@ struct unary_tensor_expression */ constexpr unary_tensor_expression(unary_tensor_expression&& l) noexcept = default; - /// @brief This the only way to access the protected move constructor of other expressions. + /// @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; diff --git a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp index fcc357529..1248a6563 100644 --- a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp +++ b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp @@ -45,39 +45,41 @@ struct unary_tensor_expression; namespace boost::numeric::ublas::detail { -template +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 - : std::integral_constant< bool, same_exp > + : is_tensor_type {}; -template -static constexpr bool has_tensor_types_v = has_tensor_types< std::decay_t, std::decay_t >::value; +template +static constexpr bool has_tensor_types_v = has_tensor_types< std::decay_t >::value; template -struct has_tensor_types> -{ - static constexpr bool value = - same_exp || - 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 = - same_exp || - same_exp || - 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 = - same_exp || - has_tensor_types >::value; -}; +struct has_tensor_types< unary_tensor_expression > + : has_tensor_types< std::decay_t > +{}; } // namespace boost::numeric::ublas::detail @@ -85,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(); } @@ -103,17 +112,14 @@ 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(has_tensor_types_v>, + static_assert(has_tensor_types_v>, "Error in boost::numeric::ublas::detail::retrieve_extents: Expression to evaluate should contain tensors."); auto const& cast_expr = expr(); - - if constexpr ( same_exp ) - return cast_expr.extents(); - else - return retrieve_extents(cast_expr); + + return retrieve_extents(cast_expr); } // Disable warning for unreachable code for MSVC compiler @@ -129,24 +135,24 @@ 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(has_tensor_types_v>, + static_assert(has_tensor_types_v>, "Error in boost::numeric::ublas::detail::retrieve_extents: Expression to evaluate should contain tensors."); auto const& lexpr = expr.left_expr(); auto const& rexpr = expr.right_expr(); - if constexpr ( same_exp ) + if constexpr ( is_tensor_type_v ) return lexpr.extents(); - else if constexpr ( same_exp ) + else if constexpr ( is_tensor_type_v ) return rexpr.extents(); - else if constexpr ( has_tensor_types_v ) + else if constexpr ( has_tensor_types_v) return retrieve_extents(lexpr); - - else if constexpr ( has_tensor_types_v ) + + else if constexpr ( has_tensor_types_v) return retrieve_extents(rexpr); } @@ -162,19 +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(has_tensor_types_v>, + 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 ( same_exp ) - return uexpr.extents(); - - else if constexpr ( has_tensor_types_v ) - return retrieve_extents(uexpr); + return retrieve_extents(uexpr); } } // namespace boost::numeric::ublas::detail @@ -184,91 +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(has_tensor_types_v>, + static_assert(has_tensor_types_v>, "Error in boost::numeric::ublas::all_extents_equal: Expression to evaluate should contain tensors."); auto const& cast_expr = expr(); - using ::operator==; - using ::operator!=; - - if constexpr ( same_exp ) - if( e != cast_expr.extents() ) - return false; - - if constexpr ( has_tensor_types_v ) - if ( !all_extents_equal(cast_expr, e)) - return false; - - return true; - + 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(has_tensor_types_v>, + static_assert(has_tensor_types_v>, "Error in boost::numeric::ublas::all_extents_equal: Expression to evaluate should contain tensors."); - using ::operator==; - using ::operator!=; - auto const& lexpr = expr.left_expr(); auto const& rexpr = expr.right_expr(); - if constexpr ( same_exp ) - if(e != lexpr.extents()) - return false; - - if constexpr ( same_exp ) - if(e != rexpr.extents()) - return false; - - if constexpr ( has_tensor_types_v ) - if(!all_extents_equal(lexpr, e)) - return false; - - if constexpr ( has_tensor_types_v ) - if(!all_extents_equal(rexpr, 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(has_tensor_types_v>, + static_assert(has_tensor_types_v>, "Error in boost::numeric::ublas::all_extents_equal: Expression to evaluate should contain tensors."); - using ::operator==; - auto const& uexpr = expr.expr(); - if constexpr ( same_exp ) - if(e != uexpr.extents()) - return false; - - if constexpr ( has_tensor_types_v ) - if(!all_extents_equal(uexpr, e)) - return false; - - return true; + return all_extents_equal(uexpr, e); } } // namespace boost::numeric::ublas::detail diff --git a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp index 4328b1c07..fec0f42d3 100644 --- a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp +++ b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp @@ -191,30 +191,18 @@ template (boost::numeric::ublas::detail::TensorExpression) && (boost::numeric::ublas::detail::TensorExpression) ) -inline constexpr auto operator+( EL&& lhs, ER&& rhs ) - noexcept( - std::is_same_v< - boost::numeric::ublas::detail::real_expression_type_t, - boost::numeric::ublas::detail::real_expression_type_t - > - ) +inline constexpr auto operator+( EL&& lhs, ER&& rhs ) noexcept { 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< typename ltensor_t::value_type, 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 ( std::forward(lhs), std::forward(rhs), [](auto const& l, auto const& r){ return l + r; } ); @@ -226,30 +214,18 @@ template (boost::numeric::ublas::detail::TensorExpression) && (boost::numeric::ublas::detail::TensorExpression) ) -inline constexpr auto operator-( EL&& lhs, ER&& rhs ) - noexcept( - std::is_same_v< - boost::numeric::ublas::detail::real_expression_type_t, - boost::numeric::ublas::detail::real_expression_type_t - > - ) +inline constexpr auto operator-( EL&& lhs, ER&& rhs ) noexcept { 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< typename ltensor_t::value_type, 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 ( std::forward(lhs), std::forward(rhs), [](auto const& l, auto const& r){ return l - r; } ); @@ -260,30 +236,18 @@ template (boost::numeric::ublas::detail::TensorExpression) && (boost::numeric::ublas::detail::TensorExpression) ) -inline constexpr auto operator*( EL&& lhs, ER&& rhs ) - noexcept( - std::is_same_v< - boost::numeric::ublas::detail::real_expression_type_t, - boost::numeric::ublas::detail::real_expression_type_t - > - ) +inline constexpr auto operator*( EL&& lhs, ER&& rhs ) noexcept { 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< typename ltensor_t::value_type, 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 ( std::forward(lhs), std::forward(rhs), [](auto const& l, auto const& r){ return l * r; } ); @@ -294,30 +258,18 @@ template (boost::numeric::ublas::detail::TensorExpression) && (boost::numeric::ublas::detail::TensorExpression) ) -inline constexpr auto operator/( EL&& lhs, ER&& rhs ) - noexcept( - std::is_same_v< - boost::numeric::ublas::detail::real_expression_type_t, - boost::numeric::ublas::detail::real_expression_type_t - > - ) +inline constexpr auto operator/( EL&& lhs, ER&& rhs ) noexcept { 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< typename ltensor_t::value_type, 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 ( std::forward(lhs), std::forward(rhs), [](auto const& l, auto const& r){ return l / r; } ); From b7de038dd938ee8cf3501a46aa8e7ef9f107b8d5 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Tue, 15 Feb 2022 21:36:08 +0530 Subject: [PATCH 14/18] refactor(expression): use `std::invoke` and add types to functional objects to catch type errors --- .../boost/numeric/ublas/tensor/expression.hpp | 6 +- .../ublas/tensor/operators_arithmetic.hpp | 83 +++++++++++------ .../ublas/tensor/operators_comparison.hpp | 93 ++++++++++++++----- 3 files changed, 129 insertions(+), 53 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/expression.hpp b/include/boost/numeric/ublas/tensor/expression.hpp index 6e70699b9..edf7c03a7 100644 --- a/include/boost/numeric/ublas/tensor/expression.hpp +++ b/include/boost/numeric/ublas/tensor/expression.hpp @@ -181,8 +181,8 @@ struct binary_tensor_expression [[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 op(left_expr()(i), right_expr()(i)); } + [[nodiscard]] inline constexpr + decltype(auto) operator()(size_type i) const { return std::invoke(op, left_expr()(i), right_expr()(i)); } protected: /** @@ -255,7 +255,7 @@ struct unary_tensor_expression [[nodiscard]] constexpr auto const& expr() const noexcept{ return cast_tensor_expression(e); } [[nodiscard]] inline constexpr - decltype(auto) operator()(size_type i) const { return op(expr()(i)); } + decltype(auto) operator()(size_type i) const { return std::invoke(op, expr()(i)); } protected: /** diff --git a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp index fec0f42d3..6bd385870 100644 --- a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp +++ b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp @@ -76,9 +76,10 @@ inline constexpr auto operator*( EL&& 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_binary_tensor_expression( - std::forward(lhs), std::forward(rhs), std::multiplies<>{} + std::forward(lhs), std::forward(rhs), std::multiplies{} ); } @@ -91,9 +92,10 @@ inline constexpr auto operator+( EL&& 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_binary_tensor_expression( - std::forward(lhs), std::forward(rhs), std::plus<>{} + std::forward(lhs), std::forward(rhs), std::plus{} ); } @@ -106,9 +108,10 @@ inline constexpr auto operator-( EL&& 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_binary_tensor_expression( - std::forward(lhs), std::forward(rhs), std::minus<>{} + std::forward(lhs), std::forward(rhs), std::minus{} ); } @@ -121,9 +124,10 @@ inline constexpr auto operator/( EL&& 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_binary_tensor_expression( - std::forward(lhs), std::forward(rhs), std::divides<>{} + std::forward(lhs), std::forward(rhs), std::divides{} ); } @@ -136,9 +140,10 @@ inline constexpr auto operator*( EL&& 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_binary_tensor_expression( - std::forward(lhs), std::forward(rhs), std::multiplies<>{} + std::forward(lhs), std::forward(rhs), std::multiplies{} ); } @@ -151,9 +156,10 @@ inline constexpr auto operator+( EL&& 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_binary_tensor_expression( - std::forward(lhs), std::forward(rhs), std::plus<>{} + std::forward(lhs), std::forward(rhs), std::plus{} ); } @@ -166,9 +172,10 @@ inline constexpr auto operator-( EL&& 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_binary_tensor_expression( - std::forward(lhs), std::forward(rhs), std::minus<>{} + std::forward(lhs), std::forward(rhs), std::minus{} ); } @@ -180,9 +187,10 @@ template inline constexpr auto operator/( EL&& 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_binary_tensor_expression( - std::forward(lhs), std::forward(rhs), std::divides<>{} + std::forward(lhs), std::forward(rhs), std::divides{} ); } @@ -204,7 +212,7 @@ inline constexpr auto operator+( EL&& lhs, ER&& rhs ) noexcept ); return boost::numeric::ublas::detail::make_binary_tensor_expression ( - std::forward(lhs), std::forward(rhs), [](auto const& l, auto const& r){ return l + r; } + std::forward(lhs), std::forward(rhs), std::plus{} ); } @@ -227,7 +235,7 @@ inline constexpr auto operator-( EL&& lhs, ER&& rhs ) noexcept ); return boost::numeric::ublas::detail::make_binary_tensor_expression ( - std::forward(lhs), std::forward(rhs), [](auto const& l, auto const& r){ return l - r; } + std::forward(lhs), std::forward(rhs), std::minus{} ); } @@ -249,7 +257,7 @@ inline constexpr auto operator*( EL&& lhs, ER&& rhs ) noexcept ); return boost::numeric::ublas::detail::make_binary_tensor_expression ( - std::forward(lhs), std::forward(rhs), [](auto const& l, auto const& r){ return l * r; } + std::forward(lhs), std::forward(rhs), std::multiplies{} ); } @@ -271,7 +279,7 @@ inline constexpr auto operator/( EL&& lhs, ER&& rhs ) noexcept ); return boost::numeric::ublas::detail::make_binary_tensor_expression ( - std::forward(lhs), std::forward(rhs), [](auto const& l, auto const& r){ return l / r; } + std::forward(lhs), std::forward(rhs), std::divides{} ); } @@ -284,10 +292,11 @@ inline constexpr auto operator+( 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](auto const& r){ return lhs + r; } + [lhs](value_type const& r){ return lhs + r; } ); } @@ -298,10 +307,11 @@ inline constexpr auto operator-( 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](auto const& r){ return lhs - r; } + [lhs](value_type const& r){ return lhs - r; } ); } @@ -312,10 +322,11 @@ inline constexpr auto operator*( 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](auto const& r){ return lhs * r; } + [lhs](value_type const& r){ return lhs * r; } ); } @@ -326,10 +337,11 @@ inline constexpr auto operator/( 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](auto const& r){ return lhs / r; } + [lhs](value_type const& r){ return lhs / r; } ); } @@ -340,10 +352,11 @@ inline constexpr auto operator+( 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] (auto const& l) { return l + rhs; } + [rhs] (value_type const& l) { return l + rhs; } ); } @@ -354,10 +367,11 @@ inline constexpr auto operator-( 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] (auto const& l) { return l - rhs; } + [rhs] (value_type const& l) { return l - rhs; } ); } @@ -368,10 +382,11 @@ inline constexpr auto operator*( 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] (auto const& l) { return l * rhs; } + [rhs] (value_type const& l) { return l * rhs; } ); } @@ -382,10 +397,11 @@ inline constexpr auto operator/( 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] (auto const& l) { return l / rhs; } + [rhs] (value_type const& l) { return l / rhs; } ); } @@ -394,7 +410,8 @@ inline constexpr auto& operator += ( boost::numeric::ublas::tensor_core& lhs, boost::numeric::ublas::detail::tensor_expression,D> const& expr ){ - boost::numeric::ublas::detail::eval(lhs, expr(), [](auto& l, auto const& r) { l+=r; } ); + 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; } @@ -403,7 +420,8 @@ 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; } ); + 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; } @@ -412,7 +430,8 @@ 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; } ); + 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; } @@ -421,7 +440,8 @@ 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; } ); + 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; } @@ -433,7 +453,8 @@ inline constexpr auto& operator += ( boost::numeric::ublas::tensor_core& lhs, typename boost::numeric::ublas::tensor_core::value_type r ){ - boost::numeric::ublas::detail::eval(lhs, [r](auto& l) { l+=r; } ); + 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; } @@ -442,7 +463,8 @@ inline constexpr auto& operator -= ( boost::numeric::ublas::tensor_core& lhs, typename boost::numeric::ublas::tensor_core::value_type r ){ - boost::numeric::ublas::detail::eval(lhs, [r](auto& l) { l-=r; } ); + 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; } @@ -451,7 +473,8 @@ inline constexpr auto& operator *= ( boost::numeric::ublas::tensor_core& lhs, typename boost::numeric::ublas::tensor_core::value_type r ){ - boost::numeric::ublas::detail::eval(lhs, [r](auto& l) { l*=r; } ); + 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; } @@ -460,7 +483,8 @@ inline constexpr auto& operator /= ( boost::numeric::ublas::tensor_core& lhs, typename boost::numeric::ublas::tensor_core::value_type r ){ - boost::numeric::ublas::detail::eval(lhs, [r](auto& l) { l/=r; } ); + 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; } @@ -476,8 +500,9 @@ template requires boost::numeric::ublas::detail::TensorExpression inline constexpr auto operator -(E&& e) { 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<>{} + std::forward(e), std::negate{} ); } diff --git a/include/boost/numeric/ublas/tensor/operators_comparison.hpp b/include/boost/numeric/ublas/tensor/operators_comparison.hpp index fa91ee29c..c2ab54c59 100644 --- a/include/boost/numeric/ublas/tensor/operators_comparison.hpp +++ b/include/boost/numeric/ublas/tensor/operators_comparison.hpp @@ -26,15 +26,44 @@ namespace boost::numeric::ublas { template class tensor_core; + } // namespace boost::numeric::ublas namespace boost::numeric::ublas::detail { +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]] inline constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) noexcept - requires ( same_exp< BinaryPred, std::equal_to<> > || same_exp< BinaryPred, std::not_equal_to<> > ) + requires is_equality_functional_object_v { auto const& lexpr = cast_tensor_expression(lhs); @@ -98,6 +127,7 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression > && is_static_v< std::decay_t< decltype(retrieve_extents(rhs)) > > ) + requires ( not is_equality_functional_object_v ) { auto const& lexpr = cast_tensor_expression(lhs); auto const& rexpr = cast_tensor_expression(rhs); @@ -203,16 +233,20 @@ template [[nodiscard]] inline constexpr bool operator==( boost::numeric::ublas::detail::tensor_expression const& lhs, - boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ - 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) noexcept{ - 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 @@ -224,7 +258,8 @@ constexpr auto operator< ( 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)) > > ) { - return boost::numeric::ublas::detail::compare( lhs, rhs, std::less<>{} ); + using value_type = typename T1::value_type; + return boost::numeric::ublas::detail::compare( lhs, rhs, std::less{} ); } template @@ -236,7 +271,8 @@ constexpr auto operator<=( 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)) > > ) { - return boost::numeric::ublas::detail::compare( lhs, rhs, std::less_equal<>{} ); + using value_type = typename T1::value_type; + return boost::numeric::ublas::detail::compare( lhs, rhs, std::less_equal{} ); } template @@ -248,7 +284,8 @@ constexpr auto operator> ( 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)) > > ) { - return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater<>{} ); + using value_type = typename T1::value_type; + return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater{} ); } template @@ -260,7 +297,8 @@ constexpr auto operator>=( 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)) > > ) { - return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater_equal<>{} ); + using value_type = typename T1::value_type; + return boost::numeric::ublas::detail::compare( lhs, rhs, std::greater_equal{} ); } @@ -270,64 +308,77 @@ constexpr auto operator>=( template [[nodiscard]] inline constexpr bool operator==( typename T::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs == r; } ); + 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::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs != r; } ); + 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::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs < r; } ); + 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::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs <= r; } ); + 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::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs > r; } ); + 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::value_type lhs, boost::numeric::ublas::detail::tensor_expression const& rhs) noexcept{ - return boost::numeric::ublas::detail::compare( rhs, [lhs](auto const& r){ return lhs >= r; } ); + 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 bool operator==( boost::numeric::ublas::detail::tensor_expression const& lhs, typename T::value_type rhs) noexcept{ - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l == rhs; } ); + 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::value_type rhs) noexcept{ - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l != rhs; } ); + 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::value_type rhs) noexcept{ - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l < rhs; } ); + 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::value_type rhs) noexcept{ - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l <= rhs; } ); + 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::value_type rhs) noexcept{ - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l > rhs; } ); + 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::value_type rhs) noexcept{ - return boost::numeric::ublas::detail::compare( lhs, [rhs](auto const& l){ return l >= rhs; } ); + using value_type = typename T::value_type; + return boost::numeric::ublas::detail::compare( lhs, [rhs](value_type const& l){ return l >= rhs; } ); } From bc0afbd0322ef69968ded9c63fd41927de490a6b Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Wed, 16 Feb 2022 14:19:03 +0530 Subject: [PATCH 15/18] refactor(expression): add `noexcept`, rearrange constructors, and remove redundant casting --- .../boost/numeric/ublas/tensor/expression.hpp | 18 +++++++----- .../ublas/tensor/expression_evaluation.hpp | 15 ++++++++-- .../ublas/tensor/operators_arithmetic.hpp | 29 ++++++++++--------- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/expression.hpp b/include/boost/numeric/ublas/tensor/expression.hpp index edf7c03a7..31893c1d1 100644 --- a/include/boost/numeric/ublas/tensor/expression.hpp +++ b/include/boost/numeric/ublas/tensor/expression.hpp @@ -122,13 +122,14 @@ struct tensor_expression inline constexpr auto const& operator()() const noexcept { return *static_cast (this); } - - constexpr ~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& operator=(const tensor_expression&) = delete; + constexpr tensor_expression& operator=(tensor_expression&&) noexcept = delete; + + constexpr ~tensor_expression() = default; protected : /** @@ -171,12 +172,14 @@ struct binary_tensor_expression , er(std::forward(r)) , op(std::forward(o)) {} - constexpr ~binary_tensor_expression() = default; /// @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); } @@ -237,20 +240,21 @@ struct unary_tensor_expression using derived_type = tensor_expression >; using size_type = typename tensor_type::size_type; + + 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)) {} - constexpr ~unary_tensor_expression() = default; - - constexpr unary_tensor_expression() = delete; - + /// @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; + 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); } diff --git a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp index 1248a6563..d6af23c61 100644 --- a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp +++ b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp @@ -262,6 +262,10 @@ template 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)) > > + ) { using rtensor_t = typename std::decay_t::tensor_type; using ltensor_t = tensor_core; @@ -301,10 +305,14 @@ inline void eval(tensor_core& lhs, TensorExpression auto const& ex */ 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)) > > + ) { - eval(lhs, expr, [](auto& l, auto const& r){ - l = r; - }); + using value_type = typename tensor_core::value_type; + + eval(lhs, expr, [](value_type& l, value_type const& r){ l = r; }); } @@ -319,6 +327,7 @@ inline void eval(tensor_core& lhs, TensorExpression auto const& ex 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) diff --git a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp index 6bd385870..8694a1cb4 100644 --- a/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp +++ b/include/boost/numeric/ublas/tensor/operators_arithmetic.hpp @@ -411,37 +411,37 @@ inline constexpr auto& operator += ( 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; } ); + 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::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; } ); + 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::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; } ); + 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::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; } ); + boost::numeric::ublas::detail::eval(lhs, expr, [](value_type& l, value_type const& r) { l/=r; } ); return lhs; } @@ -452,7 +452,7 @@ 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; @@ -462,7 +462,7 @@ 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; @@ -472,7 +472,7 @@ 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; @@ -482,7 +482,7 @@ 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; @@ -498,9 +498,10 @@ inline constexpr template requires boost::numeric::ublas::detail::TensorExpression -inline constexpr auto operator -(E&& e) { +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{} ); @@ -515,7 +516,7 @@ inline constexpr auto operator -(E&& e) { */ 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) { @@ -528,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; From 00357a070e61aef2b9d0871133d8e192faad1542 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Wed, 16 Feb 2022 17:59:34 +0530 Subject: [PATCH 16/18] fix(test): remove the undefined behaviour if the type is not FP The standard does not define the complex class other than float, double, and long double. It leaves the implementation on the hands of vendors, and it could lead to the portability issue or undefined behaviour, which is not warranted. --- .../functions/test_functions_real_imag_conj.cpp | 12 ++++++------ test/tensor/utility.hpp | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) 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/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; From 549f8a044974284ee59f98eea0462832b7d358ba Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Wed, 16 Feb 2022 18:02:02 +0530 Subject: [PATCH 17/18] refactor(expression): improve error message and simplified if-else. --- .../ublas/tensor/expression_evaluation.hpp | 32 +++++++---- .../ublas/tensor/operators_comparison.hpp | 54 +++++++------------ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp index d6af23c61..9269c6e32 100644 --- a/include/boost/numeric/ublas/tensor/expression_evaluation.hpp +++ b/include/boost/numeric/ublas/tensor/expression_evaluation.hpp @@ -267,33 +267,41 @@ inline void eval(tensor_core& lhs, TensorExpression auto const& ex is_static_v< std::decay_t< decltype(retrieve_extents(expr)) > > ) { - using rtensor_t = typename std::decay_t::tensor_type; + auto const& rhs = cast_tensor_expression(expr); + using ltensor_t = tensor_core; using lvalue_type = typename ltensor_t::value_type; - using rvalue_type = typename rtensor_t::value_type; using lextents_t = typename ltensor_t::extents_type; - using rextents_t = typename rtensor_t::extents_type; + using rvalue_type = std::decay_t< decltype(rhs(0)) >; + using rextents_t = std::decay_t< decltype(retrieve_extents(expr)) >; static_assert(std::is_same_v, - "boost::numeric::ublas::detail::eval(tensor_core&, TensorExpression auto const&) : " + "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 constexpr(is_static_v && is_static_v){ static_assert(std::is_same_v, - "boost::numeric::ublas::tensor_core: " + "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("Error in boost::numeric::ublas::tensor_core: expression contains tensors with different shapes."); + 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& rhs = cast_tensor_expression(expr); + 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) + for(auto i = 0u; i < size; ++i) std::invoke(fn, lhs(i), rhs(i)); } @@ -329,8 +337,14 @@ template inline void eval(tensor_core& lhs, UnaryFn&& fn) noexcept( is_static_v< std::decay_t< decltype(retrieve_extents(lhs)) > > ) { + 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) + for(auto i = 0u; i < size; ++i) std::invoke(fn, lhs(i)); } diff --git a/include/boost/numeric/ublas/tensor/operators_comparison.hpp b/include/boost/numeric/ublas/tensor/operators_comparison.hpp index c2ab54c59..16193ac96 100644 --- a/include/boost/numeric/ublas/tensor/operators_comparison.hpp +++ b/include/boost/numeric/ublas/tensor/operators_comparison.hpp @@ -73,14 +73,14 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression, - "boost::numeric::ublas::detail::compare : " + "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" ); static_assert( std::is_invocable_r_v, - "boost::numeric::ublas::detail::compare(lhs,rhs,pred) :" - "predicate must be a binary predicate, and it must return a bool" + "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); @@ -97,20 +97,16 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression; if constexpr(is_static_v< lex_t > && is_static_v< rex_t >){ - if constexpr(!same_exp< lex_t, rex_t >) - return { false, size_type{} }; - - return { true, product_v< lex_t > }; + constexpr bool is_same = same_exp< lex_t, rex_t >; + return { is_same, is_same ? product_v< lex_t > : size_type{} }; } else { - if(::operator!=(le,re)) - return { false, size_type{} }; - - return { true, product( le ) }; + bool const is_same = ::operator==(le,re); + return { is_same, is_same ? product( le ) : size_type{} }; } }; auto const [status, size] = cal_size(le, re); - + for(auto i = size_type{}; i < size; ++i){ if(!std::invoke(pred, lexpr(i), rexpr(i))) return false; @@ -136,14 +132,14 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression, - "boost::numeric::ublas::detail::compare : " + "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" ); static_assert( std::is_invocable_r_v, - "boost::numeric::ublas::detail::compare(lhs,rhs,pred) :" - "predicate must be a binary predicate, and it must return a bool" + "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); @@ -160,7 +156,7 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression && is_static_v< rex_t >){ static_assert(same_exp< lex_t, rex_t >, - "boost::numeric::ublas::detail::compare : " + "boost::numeric::ublas::detail::compare(tensor_expresion const& lhs, tensor_expresion const& rhs, BinaryFn&& pred) : " "cannot compare tensors with different shapes." ); @@ -168,7 +164,7 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression const& expr, UnaryPred&& pred) noe auto const& ue = cast_tensor_expression(expr); auto const& e = retrieve_extents(ue); - using size_type = typename T::size_type; + 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(expr,pred) :" - "predicate must be an unary predicate, and it must return a bool" + 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" ); - // returns the size of the container - constexpr auto cal_size = [](auto const& e) - -> size_type - { - using extents_t = std::decay_t< decltype(e) >; - - if constexpr(is_static_v< extents_t >) - return product_v< extents_t >; - else - return product( e ); - }; - - size_type const size = cal_size(e); + 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))) From 23ba8fc0c18e1286494cf18f8d40207a3c85a4fa Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Thu, 17 Feb 2022 00:36:31 +0530 Subject: [PATCH 18/18] refactor(compare): combine two compare function into one for easier maintainability --- .../ublas/tensor/operators_comparison.hpp | 131 +++++++----------- 1 file changed, 49 insertions(+), 82 deletions(-) diff --git a/include/boost/numeric/ublas/tensor/operators_comparison.hpp b/include/boost/numeric/ublas/tensor/operators_comparison.hpp index 16193ac96..179c54f4c 100644 --- a/include/boost/numeric/ublas/tensor/operators_comparison.hpp +++ b/include/boost/numeric/ublas/tensor/operators_comparison.hpp @@ -60,70 +60,62 @@ struct is_equality_functional_object< std::not_equal_to<> > : std::true_type {}; -template -[[nodiscard]] inline -constexpr bool compare(tensor_expression const& lhs, tensor_expression const& rhs, BinaryPred&& pred) noexcept - requires is_equality_functional_object_v +template +[[nodiscard]] +constexpr auto compare_helper(LE const& le, RE const& re, std::true_type /*unused*/) noexcept + -> std::pair { - - 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" - ); - - 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 ::operator==; + + 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 }; + } +} - // returns the pair containing false if extents are not equal - // else true, and the size of the container. - constexpr auto cal_size = [](auto const& le, auto const& re) - -> std::pair - { - using lex_t = std::decay_t< decltype(le) >; - using rex_t = std::decay_t< decltype(re) >; - - if constexpr(is_static_v< lex_t > && is_static_v< rex_t >){ - constexpr bool is_same = same_exp< lex_t, rex_t >; - return { is_same, is_same ? product_v< lex_t > : size_type{} }; - } else { - bool const is_same = ::operator==(le,re); - return { is_same, is_same ? product( le ) : size_type{} }; +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_helper(Lextents const& lhs, Rextents const& rhs) : " + "cannot compare tensors with different shapes." + ); } - }; - auto const [status, size] = cal_size(le, re); - - for(auto i = size_type{}; i < size; ++i){ - if(!std::invoke(pred, lexpr(i), rexpr(i))) - return false; + SizeType const size = product( le ); + return { true, size }; } - - // return false if the status is false - return ( true & status ); } template [[nodiscard]] inline 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_static_v< std::decay_t< decltype(retrieve_extents(lhs)) > > && + is_static_v< std::decay_t< decltype(retrieve_extents(rhs)) > > + ) || is_equality_functional_object_v ) - requires ( not is_equality_functional_object_v ) { auto const& lexpr = cast_tensor_expression(lhs); auto const& rexpr = cast_tensor_expression(rhs); @@ -146,41 +138,16 @@ constexpr bool compare(tensor_expression const& lhs, tensor_expression, std::true_type, std::false_type >; - // returns the size of the container - constexpr auto cal_size = [](auto const& le, auto const& re) - -> size_type - { - using lex_t = std::decay_t< decltype(le) >; - using rex_t = std::decay_t< decltype(re) >; - - if constexpr(is_static_v< lex_t > && is_static_v< rex_t >){ - static_assert(same_exp< lex_t, rex_t >, - "boost::numeric::ublas::detail::compare(tensor_expresion const& lhs, tensor_expresion const& rhs, BinaryFn&& pred) : " - "cannot compare tensors with different shapes." - ); - - return product_v< lex_t >; - }else{ - if(::operator!=(le,re)){ - throw std::runtime_error( - "boost::numeric::ublas::detail::compare(tensor_expresion const& lhs, tensor_expresion const& rhs, BinaryFn&& pred) : " - "cannot compare tensors with different shapes." - ); - } - - return product( le ); - } - }; - - size_type const size = cal_size(le, re); - + auto const [status, size] = compare_helper(le, re, is_eq_t{}); + for(auto i = size_type{}; i < size; ++i){ if(!std::invoke(pred, lexpr(i), rexpr(i))) return false; } - return true; + return status; } @@ -201,7 +168,7 @@ constexpr bool compare(tensor_expression const& expr, UnaryPred&& pred) noe "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 );; + 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)))