Created
June 6, 2019 15:54
-
-
Save coder3101/8ce7a868910c099eec8ca8a50ea31b00 to your computer and use it in GitHub Desktop.
Sample impl of YAP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <boost/yap/expression.hpp> | |
#include <algorithm> | |
#include <cassert> | |
#include <iostream> | |
#include <vector> | |
// TAKEN FROM BOOST.YAP EXAMPLES, BUT MODIFIED BY CODER3101 | |
/* | |
* Every YAP expression be it any type must meet this concept. | |
* - It must have a static const member named kind | |
* - It must have a public member named elements | |
*/ | |
template <boost::yap::expr_kind Kind, typename Tuple> | |
struct lazy_vector_expr; | |
/* | |
* This is a transform that extracts ith value from terminal node of YAP | |
*/ | |
struct take_nth | |
{ | |
boost::yap::terminal<lazy_vector_expr, double> | |
operator() (boost::yap::terminal<lazy_vector_expr, std::vector<double>> const & expr); | |
std::size_t n; | |
}; | |
/* | |
* We finally create the lazy_vector_expr class following the | |
* YAP concepts as mentioned above. | |
* | |
* Few Important things to mention : | |
* - kind is a enum of boost::yap::expr_kind. This value denotes | |
* the operand that has been applied. So if this is expr_kind::plus. | |
* It means that this node in AST represents a plus operation. | |
* | |
* - Tuple is a boost::hana<..> type. YAP uses hana because it provides | |
* compile time tuple, necessary to build expression templates. This tuple | |
* holds the operands to which the expr_kind::kind operator will be applied. | |
* | |
* * If kind = expr_kind::terminal, this means that Tuple elements | |
* only has one value that is terminal node of AST. Since boost.yap has some | |
* limitations, if expr_kind::terminal it is required that elements must not have | |
* a value that can be converted to any YAP expression concept. It is due to this reason | |
* that we cannot follow CRTP pattern, we want terminal to have some value that cannot be | |
* converted to YAP expression. In this case we use std::vector<>, which is enough to represent | |
* the complete lazy_vector. | |
*/ | |
template <boost::yap::expr_kind Kind, typename Tuple> | |
struct lazy_vector_expr | |
{ | |
static const boost::yap::expr_kind kind = Kind; | |
Tuple elements; | |
// Note that this does not return an expression; it is greedily evaluated. | |
auto operator[] (std::size_t n) const; | |
}; | |
// Some Operator Overloads. | |
BOOST_YAP_USER_BINARY_OPERATOR(plus, lazy_vector_expr, lazy_vector_expr) | |
BOOST_YAP_USER_BINARY_OPERATOR(minus, lazy_vector_expr, lazy_vector_expr) | |
/* | |
* This runs the transform and evaluates the terminal expression returned from | |
* the transform. | |
*/ | |
template <boost::yap::expr_kind Kind, typename Tuple> | |
auto lazy_vector_expr<Kind, Tuple>::operator[] (std::size_t n) const | |
{ return boost::yap::evaluate(boost::yap::transform(*this, take_nth{n})); } | |
/** | |
* Defines the transform to extract the ith element from terminal node | |
* This then wraps it into a terminal expression type for furthur lazy evaluation | |
* if desired. | |
*/ | |
boost::yap::terminal<lazy_vector_expr, double> | |
take_nth::operator() (boost::yap::terminal<lazy_vector_expr, std::vector<double>> const & expr) | |
{ | |
double x = boost::yap::value(expr)[n]; | |
return boost::yap::make_terminal<lazy_vector_expr, double>(std::move(x)); | |
} | |
/* | |
* This is most Important section. Here we create the lazy_vector class. | |
* Because we want that this template class to be trivially convertible to YAP expression terminal. | |
* We inherit this class from a terminal type of lazy_vector_expr. | |
* | |
* Since, lazy_vector now is trivially convertible to an YAP expression, we need to put something else | |
* into the terminal node of lazy_vector_expr. In this case we have choosen to put | |
* std::vector<double> into the terminal. | |
* The thing that we want to put in the terminal must meet following : | |
* - It must have enough information so that expression values can be evaluated greedly. | |
* - It must have enough information so that we can infer the dimension or shape of expression greedly. | |
* In this case since it was a vector, std::vector<> values and .size() are enough to | |
* satisfy the needs. In case of matrix or tensor, we may need to put more than one thing | |
* say dimension/extent and data. While we can surely put multiple things into terminal, | |
* it is usually good to wrap all those things into one single class like matrix/tensor_core. | |
* We then put that core into the terminal. Now the Question how do we access the contents of | |
* what we have placed in the terminal? See the in-class comments. | |
*/ | |
struct lazy_vector : public lazy_vector_expr< | |
boost::yap::expr_kind::terminal, | |
boost::hana::tuple<std::vector<double>>> | |
{ | |
lazy_vector () {} | |
explicit lazy_vector (std::vector<double> && vec) | |
{ | |
/* | |
* Since this elements is a hana::tuple of std::vector<double>, | |
* we can assign it a new value at construction. We cannot do it at | |
* initializer list however because elements does not belong to this class. | |
*/ | |
elements = boost::hana::tuple<std::vector<double>>(std::move(vec)); | |
} | |
auto get_data() const { | |
/* | |
* To access the elements, we use hana literals and at 0th index of elements | |
* lies the std::vector we have stored. | |
*/ | |
using namespace boost::hana::literals; | |
return this->elements[0_c].data(); | |
} | |
auto get_dimension() const { | |
using namespace boost::hana::literals; | |
return this->elements[0_c].size(); | |
} | |
}; | |
/** | |
* However there is a Issue with the this design. | |
* elements is a public member and we inherit it publicly. So anyone can access the std::vector | |
* We cannot make elements private because it will violate the YAP concepts. | |
* | |
* A possible solution as implemented in the tensor is that, we can make all those tensor_core datamembers | |
* private and declare tensor and tensor_expression a friend. So, its private memebers are only available in | |
* those classes. | |
*/ | |
/** This is the exact short implementation of tensor_expression of new ublas::tensor.*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment