Skip to content

ublas::apply Examples

Ashar edited this page Aug 23, 2019 · 2 revisions

Signature

auto ublas::apply(Expression && e, Callable ... cs);
  • Expression is any tensor-expression whose all terminal/leaf nodes are either tensors, vectors, or matrices. An attempt to put anything apart from these will likely fail a static_assert.
  • Callable is callable(s) like lambda or std::function. Each callable must take only one argument by constant reference and return non-void type. Failing which raises a static_assert.

Applications

  • Implementing lazy casts We do not provide any such functionality but it would be nice and easy to implement using this utility. Consider this example :

    auto expr = int_tensor1 + int_tensor2;
    
    auto lazy_cast = ublas::apply(expr, 
                                  [](auto const & e){
                                      return static_cast<char>(e);
                                  });
    
    auto result = lazy_cast.eval();
    // Okay result is a tensor of chars
  • Implementing Special Lazy Functions You can implement many great functions lazily using this utility. Consider this example :

    auto expr = int_tensor1 + int_tensor2;
    
    auto lazy_sqrt = ublas::apply(expr, 
                                  [](auto const & e){
                                      return sqrt(e);
                                  });
    
    auto result = lazy_sqrt.eval();
    // Okay result is sqrt(tensor1 + tensor2);
    
    static_assert(std::is_same_v<typename result::value_type, double>);
    // Okay Passes, sqrt returns double.
  • Implementing Lazy Masking We have no such operator as masking or cliping tensors by values. It can be done in a efficient lazy way by this utility. Consider this Example :

    auto tensor1 = tensor<int>{shape{50,50}, data};
    // assume data is std::vector<int> of size 50*50;
    
    auto expr = ublas::apply(tensor1, 
                 [](auto const &e){
                     return e*(e > 45);
                 });
    
    // Sets a tensor element to 0 if its values is less than 45, otherwise no change.
    // This operator is sometimes called clipping / masking by value.
    
    tensor<int> ans{shape{50,50}};
    
    expr.eval_to(ans);
    
    // ans now contains the results.
  • Cascaded Calls The callable to the utility can be cascaded in which case they are called one after the other. Consider this example where we take mod of real component of an imaginary tensor.

    auto ten1 = tensor<std::complex<float>>{shape{5,6}};
    
    auto expr = ublas::apply(ten1,
                            [](auto const &e){
                            	return std::real(e);
                            },
                            [](auto const &e){
                                return abs(e);
                            });
    
    auto ans = expr.eval();
    
  • **SO MANY MORE, YOU DO IT AS YOU PLEASE **


Resolution of Callable(s)

The callable passed to the utility must be a valid and non-overloaded callable, it is because we store your callable as function pointer directly. Moreover when you pass any such callable the compiler will complain as to which overload to pass into the utility.

Consider this bad example :

auto expr = ublas::apply(old_expr, std::sqrt);
// Error : Which std::srqt()? It is overloaded

If you want to pass function and do not prefer the generic lambda syntax (recommended). Please consider wrapping your function into something that isn't overloaded.

Consider this example :

double my_sqrt(const int & ref){
    return std::sqrt(ref);
}
// Wrapped the function into something non-overloaded

auto expr = ublas::apply(old_expr, my_sqrt);
// Okay, we know this my_sqrt
Clone this wiki locally