-
Notifications
You must be signed in to change notification settings - Fork 5
YAP Expression Examples
The new expression templates work just like the old uBLAS expression templates. However, the new expression templates are much easier and give a lot of freedom to end-user (you) and to us (developers). It is therefore 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
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.
All the tensor expressions follow Boost.YAP Expression concept. Unlike the 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 and not the internal variable are copied. However, if the operand is an r-value reference 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.
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&){return 0;}
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 a lambda or equivalent as a scalar type.
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 allow the 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 the same as standard C++ casting operators as it is just a wrapper for tensor typecasting.
The tensor resulted from the cast will have
tensor::array_type
asstd::vector
always.
Do not pass an expression to for casting. It is a compile-time error. A tensor expression casting operator is in the way.
A Complete set of operator overloads are as follow:
SYB | LHS | RHS | Return type | Arity |
---|---|---|---|---|
- |
tensor/tensor_expression | N/A | tensor_expression |
Unary |
+ |
tensor/tensor_expression | N/A | tensor_expression |
Unary |
+ |
tensor/tensor_expression | Anything | tensor_expression |
Binary |
+ |
Anything | tensor/tensor_expression | tensor_expression |
Binary |
- |
tensor/tensor_expression | Anything | tensor_expression |
Binary |
- |
Anything | tensor/tensor_expression | tensor_expression |
Binary |
* |
tensor/tensor_expression | Anything | tensor_expression |
Binary |
* |
Anything | tensor/tensor_expression | tensor_expression |
Binary |
/ |
tensor/tensor_expression | Anything | tensor_expression |
Binary |
/ |
Anything | tensor/tensor_expression | tensor_expression |
Binary |
> |
tensor/tensor_expression | Anything | tensor_expression |
Binary |
> |
Anything | tensor/tensor_expression | tensor_expression |
Binary |
< |
tensor/tensor_expression | Anything | tensor_expression |
Binary |
< |
Anything | tensor/tensor_expression | tensor_expression |
Binary |
!= |
tensor/tensor_expression | Anything | tensor_expression |
Binary |
!= |
Anything | tensor/tensor_expression | tensor_expression |
Binary |
== |
tensor/tensor_expression | Anything | tensor_expression |
Binary |
== |
Anything | tensor/tensor_expression | tensor_expression |
Binary |
>= |
tensor/tensor_expression | Anything | tensor_expression |
Binary |
>= |
Anything | tensor/tensor_expression | tensor_expression |
Binary |
<= |
tensor/tensor_expression | Anything | tensor_expression |
Binary |
<= |
Anything | tensor/tensor_expression | tensor_expression |
Binary |
+= |
tensor | tensor/tensor_expression | tensor |
Binary |
-= |
tensor | tensor/tensor_expression | tensor |
Binary |
*= |
tensor | tensor/tensor_expression | tensor |
Binary |
/= |
tensor | tensor/tensor_expression | tensor |
Binary |
Assignment of expression to any tensor type or conversion of expression to boolean (in case it involves relational operators) causes the lazy evaluation of the expression. All operators including relational operators are lazy
Anything in the above table means anything that has an overload of that operator for tensor. It could include matrix expression, vector expression or scalars.
All of the above operators are lazy except for assignment operators. Also, You will not find the code for the above overloads as they are done using Boost.YAP macros . More information about the behaviour of the lazy relational operator could be found below.
The new expression template makes even relational operators lazy. Here are some benefits of this approach and some point to be cautious about when using this new feature. Let's see what happens when we write a relational expression.
tensor<int> td1 = tensor<int>{shape{5,5},2};
tensor<int> td2 = tensor<int> {shape{5,5},3};
auto expr = td1 + 1 == td2;
boost::yap::print(std::cout, expr);
Outputs
expr<==>
expr<+>
term<tensor<int> &>
term<int>=[1]
term<tensor<int> &>
So, we built expression and did not evaluate the result. So, how do we evaluate the result?
bool b = expr; // implictly evaluates to a bool
if(expr){} // implictly evaluates to a bool
BOOST_CHECK(expr); // Error at compile time.
// BOOST_CHECK() is a macro so implicit casting cannot be performed.
BOOST_CHECK((bool)(expr)); // This works
WARNING Casting is required when implicit casting is not performed by the compiler but bool is expected
The result is evaluated by expanding the expression element-wise. The result is evaluated as in this case (pseudo code):
if lhs.extent != rhs.extent:
return false
else:
for i in 0..lhs.extent.prouct():
if ! (td1[i] + 1 == td2[i]): # <== Notice expression is expanded to a predicate
return false
return true
Due to the same reason, If an expression has more than one relational operators or does not have any relational operator a runtime error is thrown if you try to convert in into a bool.
We allow you to evaluate a relational expression into a tensors. In such case the evaluation is element-wise predicate result. Consider this example:
auto a_val = {1,1,1,2,2,2};
auto b_val = {2,2,2,2,2,2};
tensor<int> a = tensor<int>{shape{3,2}, std::vector<int>(a_val)};
tensor<int> b = tensor<int>{shape{3,2}, std::vector<int>(b_val)};
tensor<bool> result = a == b;
std::cout<<std::boolalpha<<result;
std::cout<<"\n"<<(bool)(a==b);
Outputs
[ false, false, false
true, true, true ]
false
See this produces an element-wise compared and resulted tensor also, casting it to a bool produces a false result as expected. This behaviour is required for some application and also it can be used to find index at which predicate are failing.
This was not possible in the older implementation of the expression templates of the tensor.
We both would like to thank our mentor Cem for his constant support and help in achieving our goals. We always find him helpful and he was always easy to reach for help or discussion regarding the work. We would also like to thank Google for the Google Summer of Code Programme, without which all these wouldn't be possible. Lastly, we express our gratitude to our parents for helping and providing us with all the indirect or direct needs for carrying out our work nicely in our homes.
- Home
- Project Proposal
- Milestones and Tasks
- Implementation
- Documentation
- Discussions
- Examples
- Experimental features
- Project Proposal
- Milestones and Tasks
- Implementation
- Documentation
- Discussions
- Example Code