Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(lifetime): extending the lifetime of temporary objects #160

Open
wants to merge 18 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
15c4476
fix(lifetime): extending the lifetime of temporary objects
amitsingh19975 Feb 9, 2022
8f5a5d1
fix(comparision): use `same_exp` instead of `std::is_same`
amitsingh19975 Feb 10, 2022
8178c6c
refactor(comparision): decoupled `if constexpr` and cleaned-up compare.
amitsingh19975 Feb 10, 2022
9b8c4e1
test(enable): enable tests for expression templates
amitsingh19975 Feb 12, 2022
d70a701
fix(test): bind the prvalue (=3) to a variable
amitsingh19975 Feb 12, 2022
8ec747c
refactor(expression): add function cast_tensor_expression for casting
amitsingh19975 Feb 13, 2022
7a936dd
chore(expression): add `nodiscard`
amitsingh19975 Feb 13, 2022
b6a3dd8
refactor(comparision): remove the construction of a whole new tensor
amitsingh19975 Feb 13, 2022
3a013d0
fix(compare): get the value type after `cast_tensor_expression`
amitsingh19975 Feb 13, 2022
7b23092
refactor(expression): validate predicate and add noexcept
amitsingh19975 Feb 14, 2022
a90573c
refactor(eval): clean the implementation
amitsingh19975 Feb 14, 2022
2abe215
fix(expression): disable the move constructor for public use to avoid…
amitsingh19975 Feb 15, 2022
51674d5
refactor(expression): clean unnecessary code and fix the comparision …
amitsingh19975 Feb 15, 2022
b7de038
refactor(expression): use `std::invoke` and add types to functional o…
amitsingh19975 Feb 15, 2022
bc0afbd
refactor(expression): add `noexcept`, rearrange constructors, and rem…
amitsingh19975 Feb 16, 2022
00357a0
fix(test): remove the undefined behaviour if the type is not FP
amitsingh19975 Feb 16, 2022
549f8a0
refactor(expression): improve error message and simplified if-else.
amitsingh19975 Feb 16, 2022
23ba8fc
refactor(compare): combine two compare function into one for easier m…
amitsingh19975 Feb 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions examples/tensor/simple_expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,53 @@ int main()
std::cout << "% --------------------------- " << std::endl << std::endl;
std::cout << "F=" << F << ";" << std::endl << std::endl;

// Calling overloaded operators
// and mixing expression templates with prvalues, rvalues, and lvalues
{
// NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
auto G = tensor(shape{3,3}, 3.f);
amitsingh19975 marked this conversation as resolved.
Show resolved Hide resolved
auto E_9 = G + G + G;

// formatted output
std::cout << "% --------------------------- " << std::endl;
std::cout << "% --------------------------- " << std::endl << std::endl;
std::cout << "E(9)=" << tensor(E_9) << ";" << std::endl << std::endl;

// NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
auto E_6 = G + 3.f;
amitsingh19975 marked this conversation as resolved.
Show resolved Hide resolved

// formatted output
std::cout << "% --------------------------- " << std::endl;
std::cout << "% --------------------------- " << std::endl << std::endl;
std::cout << "E(6)=" << tensor(E_6) << ";" << std::endl << std::endl;

// NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
auto const four = 4.f;
auto E_10 = E_6 + four;

// formatted output
std::cout << "% --------------------------- " << std::endl;
std::cout << "% --------------------------- " << std::endl << std::endl;
std::cout << "E(10)=" << tensor(E_10) << ";" << std::endl << std::endl;

// NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
auto E_23 = E_10 + E_10 + tensor(shape{3,3}, 3.f);
amitsingh19975 marked this conversation as resolved.
Show resolved Hide resolved

// formatted output
std::cout << "% --------------------------- " << std::endl;
std::cout << "% --------------------------- " << std::endl << std::endl;
std::cout << "E(23)=" << tensor(E_23) << ";" << std::endl << std::endl;

// NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
auto E_9_7 = tensor(shape{3,3}, 5.4f) + 4.3f;
amitsingh19975 marked this conversation as resolved.
Show resolved Hide resolved
amitsingh19975 marked this conversation as resolved.
Show resolved Hide resolved

// 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;
Expand Down
215 changes: 139 additions & 76 deletions include/boost/numeric/ublas/tensor/expression.hpp
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -13,13 +14,68 @@
#define BOOST_UBLAS_TENSOR_EXPRESSIONS_HPP

#include <cstddef>
#include <type_traits>
#include <boost/numeric/ublas/expression_types.hpp>

#include "tags.hpp"

namespace boost::numeric::ublas::detail
{

template<class T, class E>
struct tensor_expression;

template<class T, class EL, class ER, class OP>
struct binary_tensor_expression;

template<class T, class E, class OP>
struct unary_tensor_expression;

template<typename E1, typename E2>
concept same_exp = std::is_same_v< std::decay_t<E1>, std::decay_t<E2> >;

template<typename T>
struct does_exp_need_cast : std::false_type{};

template<typename T>
static constexpr bool does_exp_need_cast_v = does_exp_need_cast< std::decay_t<T> >::value;

template<typename E, typename T>
struct does_exp_need_cast< tensor_expression<T,E> > : std::true_type{};

template<typename E, typename T>
constexpr auto is_tensor_expression_impl(tensor_expression<T,E> const*) -> std::true_type;

constexpr auto is_tensor_expression_impl(void const*) -> std::false_type;

template<typename E>
constexpr auto is_matrix_expression_impl(matrix_expression<E> const*) -> std::true_type;

constexpr auto is_matrix_expression_impl(void const*) -> std::false_type;

template<typename E>
constexpr auto is_vector_expression_impl(vector_expression<E> const*) -> std::true_type;

constexpr auto is_vector_expression_impl(void const*) -> std::false_type;

template<typename E>
concept TensorExpression = decltype(is_tensor_expression_impl(static_cast<std::decay_t<E> const*>(nullptr)))::value;

// TODO: Remove this concept in the future when we have our own implementation of matrices.
template<typename E>
concept MatrixExpression = decltype(is_matrix_expression_impl(static_cast<std::decay_t<E> const*>(nullptr)))::value;

// TODO: Remove this concept in the future when we have our own implementation of vectors.
template<typename E>
concept VectorExpression = decltype(is_vector_expression_impl(static_cast<std::decay_t<E> const*>(nullptr)))::value;

template<typename Exp>
using expression_operand_t = std::conditional_t<
!std::is_lvalue_reference_v<Exp>,
std::decay_t<Exp>,
std::add_lvalue_reference_t< std::add_const_t< std::decay_t<Exp> > >
>;

/** @\brief base class for tensor expressions
*
* \note implements crtp - no use of virtual function calls
Expand All @@ -30,7 +86,7 @@ namespace boost::numeric::ublas::detail
**/
template<class T, class E>
struct tensor_expression
: public ublas_expression<E>
: public ublas_expression<E> // DISCUSS: Do we really need to do derive from ublas_expression?
{
// static const unsigned complexity = 0;
using expression_type = E;
Expand All @@ -40,11 +96,12 @@ struct tensor_expression
inline
constexpr auto const& operator()() const noexcept { return *static_cast<const expression_type*> (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 :
Expand All @@ -58,72 +115,76 @@ struct binary_tensor_expression
{
using self_type = binary_tensor_expression<T,EL,ER,OP>;
using tensor_type = T;
using binary_operation = OP;
using expression_type_left = EL;
using expression_type_right = ER;
using binary_operation = std::decay_t<OP>;
using expression_type_left = expression_operand_t<EL>;
using expression_type_right = expression_operand_t<ER>;
using derived_type = tensor_expression <tensor_type,self_type>;

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<same_exp<EL> LeftExp, same_exp<ER> RightExp, typename OPType>
explicit constexpr binary_tensor_expression(LeftExp&& l, RightExp&& r, OPType&& o)
: el(std::forward<LeftExp>(l))
, er(std::forward<RightExp>(r))
, op(std::forward<OPType>(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<expression_type_left> && does_exp_need_cast_v<expression_type_right>)
{
return op(el()(i), er()(i));
}

[[nodiscard]] inline
constexpr decltype(auto) operator()(size_type i) const
requires (does_exp_need_cast_v<expression_type_left> && !does_exp_need_cast_v<expression_type_right>)
{
return op(el()(i), er(i));
}

[[nodiscard]] inline
constexpr decltype(auto) operator()(size_type i) const
requires (!does_exp_need_cast_v<expression_type_left> && does_exp_need_cast_v<expression_type_right>)
{
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<class T1, class T2, class EL, class ER, class OP>
template<typename T, typename EL, typename ER, typename OP>
requires (
( MatrixExpression<EL> || VectorExpression<EL> || TensorExpression<EL> ) &&
( MatrixExpression<ER> || VectorExpression<ER> || TensorExpression<ER> )
)
[[nodiscard]] inline
constexpr auto make_binary_tensor_expression( tensor_expression<T1,EL> const& el, tensor_expression<T2,ER> 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<T,EL,ER,OP>(
std::forward<EL>(el),
std::forward<ER>(er),
std::forward<OP>(op)
);
return binary_tensor_expression<T1,EL,ER,OP>( el(), er(), op) ;
}

template<class T, class EL, class ER, class OP>
[[nodiscard]] inline
constexpr auto make_binary_tensor_expression( matrix_expression<EL> const& el, tensor_expression<T,ER> const& er, OP op) noexcept
{
return binary_tensor_expression<T,EL,ER,OP>( el(), er(), op) ;
}

template<class T, class EL, class ER, class OP>
[[nodiscard]] inline
constexpr auto make_binary_tensor_expression( tensor_expression<T,EL> const& el, matrix_expression<ER> const& er, OP op) noexcept
{
return binary_tensor_expression<T,EL,ER,OP>( el(), er(), op) ;
}

template<class T, class EL, class ER, class OP>
[[nodiscard]] inline
constexpr auto make_binary_tensor_expression( vector_expression<EL> const& el, tensor_expression<T,ER> const& er, OP op) noexcept
{
return binary_tensor_expression<T,EL,ER,OP>( el(), er(), op) ;
}

template<class T, class EL, class ER, class OP>
[[nodiscard]] inline
constexpr auto make_binary_tensor_expression( tensor_expression<T,EL> const& el, vector_expression<ER> const& er, OP op) noexcept
{
return binary_tensor_expression<T,EL,ER,OP>( el(), er(), op) ;
}



template<class T, class E, class OP>
struct unary_tensor_expression
Expand All @@ -132,51 +193,53 @@ struct unary_tensor_expression

using self_type = unary_tensor_expression<T,E,OP>;
using tensor_type = T;
using expression_type = E;
using unary_operation = OP;
using expression_type = expression_operand_t<E>;
using unary_operation = std::decay_t<OP>;
using derived_type = tensor_expression <T, unary_tensor_expression<T,E,OP>>;

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<same_exp<E> Exp, typename OPType>
explicit constexpr unary_tensor_expression(Exp&& ee, OPType&& o)
: e(std::forward<Exp>(ee))
, op(std::forward<OPType>(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<expression_type>
{
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<class T, class E, class OP>
[[nodiscard]] inline
constexpr auto make_unary_tensor_expression( tensor_expression<T,E> const& e, OP op) noexcept
{
return unary_tensor_expression<T,E,OP>( e() , op);
}

template<class T, class E, class OP>
[[nodiscard]] inline
constexpr auto make_unary_tensor_expression( matrix_expression<E> const& e, OP op) noexcept
{
return unary_tensor_expression<T,E,OP>( e() , op);
}

template<class T, class E, class OP>
template<typename T, typename E, typename OP>
requires ( MatrixExpression<E> || VectorExpression<E> || TensorExpression<E> )
[[nodiscard]] inline
constexpr auto make_unary_tensor_expression( vector_expression<E> const& e, OP op) noexcept
constexpr auto make_unary_tensor_expression( E&& e, OP&& op) noexcept
{
return unary_tensor_expression<T,E,OP>( e() , op);
return unary_tensor_expression<T, E, OP>(
std::forward<E>(e),
std::forward<OP>(op)
);
}


} // namespace boost::numeric::ublas::detail

#endif // BOOST_UBLAS_TENSOR_EXPRESSIONS_HPP
Loading