Skip to content

YAP Expression Examples

Ashar edited this page Jun 24, 2019 · 5 revisions

The new expression templates works just like the old uBLAS expression templates. However the new expression templates are much easier and gives a lot of freedom to end-user (you) and to us (developers). It is therefor important to point out all the capabilities and new interfaces that the new expression templates have to offer. It is highly recommended to read What's new in new expression templates before reading this wiki. This wiki will focus more on how to exploit the new features rather than talking about the new features.

tensor_expression

tensor_expression is the structure in namespace boost::numeric::ublas::detail that is returned from all the operations that are performed on the tensor types or any operation that involves any tensor-type.

You can capture your expression like this:

auto expr = 3*tensor1 + 5*tensor2;

In the following sections, we discuss some Freedoms of the new Expression template.

Freedom to move and copy expressions

All the tensor expressions follow Boost.YAP Expression concept. Unlike previous expression template, the new expression templates can be copied or moved. Allowing you to treat an expression as a regular object.

auto expr1 = tensor1 + tensor2;
auto expr2 = expr1;

tensor<int> val(expr1);
tensor<int> val2(expr2);

The tensor variables are captured by references and hence a new reference is made not and not the internal variable are copied. However if the an operand is an r-value reference the the copy of that operand is made into the new expression.

Because rvalue operands of an expression are copied. You should not create copies of expression involving rvalue operands where copying can be performance overhead.

Freedom of Scalars

When it comes to scalars, you can perform all four fundamental operations with tensors. However we have absolutely no limit or what so ever on the scalar type that you can use with tensors as long as there is an operator overload for the operation.

struct Zero_Like{};
auto operator*(int, Zero_Like&);

auto t = tensor<int>{shape{5,5}, 0};

auto expr = t * Zero_Like{};
decltype(t) t2 = expr;

In other words you can take anything as scalar as long as you provide an operator overload for the operation. Like you can even take an lambda or equivalent as an scalar type.

Freedom of Data-types and Casting

Following the trend of Freedom for Scalar, We provide freedom for internal data-types of tensor or matrix or vectors. The last expression template did not allowed user to mix-match the data-types. This is no longer true with the new expression templates: You can mix an match things as long as the operations are well defined. So,

auto td = tensor<int>{shape{5,5},0};
auto tf = tensor<float>{shape{5,5}, 1.2f};

auto expr = td + tf;

tensor<int> a = expr; // Fills a with 1
tensor<float> b = expr; // Fills b with 1.2 

All such expressions follow the exact same standard C++ behaviour for type promotion and implicit casting. We also provide 3 casting operator for tensors. They are eager casting operators and eagerly cast tensor elements and return a new tensor. They are :

  • boost::numeric::ublas::static_tensor_cast<>
  • boost::numeric::ublas::dynamic_tensor_cast<>
  • boost::numeric::ublas::reinterpret_tensor_cast<>
auto td = tensor<int>{shape{5,5}, 5};

auto tf = static_tensor_cast<float>(td);
static_assert(std::is_same_v<typename tf::value_type, float>);

try{
   auto res = dynamic_tensor_cast<float>(td);
}catch(std::bad_cast& e){
    // Indeed a bad cast
}

auto tptrs = reinterpret_tensor_cast<char*>(td); // danger here :)

They behave same as standard C++ casting operators as it is just a wrapper for tensor type casting.

The tensor resulted from the cast will have tensor::array_type as std::vector always.

Do not pass an expression to for casting. It is an compile time error. A tensor expression casting operator is in the way.

Operator Overloads

A Complete set of operator overloads are as follow:

template <class T, class F, class A>
decltype(auto) operator-(tensor<T,F,A>); // unary minus

template <class T, class F, class A>
decltype(auto) operator+(tensor<T,F,A>); // unary plus

// Tensor to anything else overload

template <class T, class F, class A, typename other>
decltype(auto) operator+(tensor<T,F,A>, other); // binary plus

template <class T, class F, class A, typename other>
decltype(auto) operator+(other, tensor<T,F,A>); // binary plus

template <class T, class F, class A, typename other>
decltype(auto) operator-(tensor<T,F,A>, other); // binary minus

template <class T, class F, class A, typename other>
decltype(auto) operator-(other, tensor<T,F,A>); // binary minus

template <class T, class F, class A, typename other>
decltype(auto) operator*(tensor<T,F,A>, other); // binary multiplies

template <class T, class F, class A, typename other>
decltype(auto) operator*(other, tensor<T,F,A>); // binary multiplies

template <class T, class F, class A, typename other>
decltype(auto) operator/(tensor<T,F,A>, other); // binary divides

template <class T, class F, class A, typename other>
decltype(auto) operator/(other, tensor<T,F,A>); // binary divides

template <class T, class F, class A, typename other>
decltype(auto) operator>(tensor<T,F,A>, other); // binary greater

template <class T, class F, class A, typename other>
decltype(auto) operator>(other, tensor<T,F,A>); // binary greater

template <class T, class F, class A, typename other>
decltype(auto) operator<(tensor<T,F,A>, other); // binary less

template <class T, class F, class A, typename other>
decltype(auto) operator<(other, tensor<T,F,A>); // binary less

template <class T, class F, class A, typename other>
decltype(auto) operator==(tensor<T,F,A>, other); // binary equal

template <class T, class F, class A, typename other>
decltype(auto) operator==(other, tensor<T,F,A>); // binary equal

template <class T, class F, class A, typename other>
decltype(auto) operator!=(tensor<T,F,A>, other); // binary not equal

template <class T, class F, class A, typename other>
decltype(auto) operator!=(other, tensor<T,F,A>); // binary not equal

template <class T, class F, class A, typename other>
decltype(auto) operator>=(tensor<T,F,A>, other); // binary greater equal

template <class T, class F, class A, typename other>
decltype(auto) operator>=(other, tensor<T,F,A>); // binary greater equal

template <class T, class F, class A, typename other>
decltype(auto) operator<=(tensor<T,F,A>, other); // binary less equal

template <class T, class F, class A, typename other>
decltype(auto) operator<=(other, tensor<T,F,A>); // binary less equal

// Tensor Expression to anything else overload

template <boost::yap::expr_kind K, typename Tuple>
decltype(auto) operator-(tensor_expression<K,Tuple>); // unary negate expr

template <boost::yap::expr_kind K, typename Tuple>
decltype(auto) operator+(tensor_expression<K,Tuple>); // unary plus expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator+(tensor_expression<K,Tuple>, other); // binary plus expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator+(other, tensor_expression<K,Tuple>); // binary plus expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator-(tensor_expression<K,Tuple>, other); // binary minus expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator-(other, tensor_expression<K,Tuple>); // binary minus expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator*(tensor_expression<K,Tuple>, other); // binary multiplies expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator*(other, tensor_expression<K,Tuple>); // binary multiplies expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator/(tensor_expression<K,Tuple>, other); // binary divides expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator/(other, tensor_expression<K,Tuple>); // binary divides expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator<(tensor_expression<K,Tuple>, other); // binary less expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator<(other, tensor_expression<K,Tuple>); // binary less expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator>(tensor_expression<K,Tuple>, other); // binary greater expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator>(other, tensor_expression<K,Tuple>); // binary greater expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator>=(tensor_expression<K,Tuple>, other); // binary greater equal expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator>=(other, tensor_expression<K,Tuple>); // binary greater equal expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator<=(tensor_expression<K,Tuple>, other); // binary less equal expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator<=(other, tensor_expression<K,Tuple>); // binary less equal expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator==(tensor_expression<K,Tuple>, other); // binary equal expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator==(other, tensor_expression<K,Tuple>); // binary equal expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator!=(tensor_expression<K,Tuple>, other); // binary not equal expr

template <boost::yap::expr_kind K, typename Tuple, typename other>
decltype(auto) operator!=(other, tensor_expression<K,Tuple>); // binary not equal expr

// Tensor Assigmenent Overload

template <class T, class F, class A, typename Expr>
decltype(auto) operator+=(tensor<T,F,A>, Expr); // add assign

template <class T, class F, class A, typename Expr>
decltype(auto) operator-=(tensor<T,F,A>, Expr); // subtract assign

template <class T, class F, class A, typename Expr>
decltype(auto) operator*=(tensor<T,F,A>, Expr); // multiply assign

template <class T, class F, class A, typename Expr>
decltype(auto) operator/=(tensor<T,F,A>, Expr); // divide assign

The return type of all of the above overload is a tensor expression

Assignment of an expression to any tensor type or conversion of an expression to boolean (in case it involves logical operators) causes the lazy evaluation of the expression.

To be continued...

Clone this wiki locally