Skip to content

Instantly share code, notes, and snippets.

@coder3101
Created June 6, 2019 15:54
Show Gist options
  • Save coder3101/8ce7a868910c099eec8ca8a50ea31b00 to your computer and use it in GitHub Desktop.
Save coder3101/8ce7a868910c099eec8ca8a50ea31b00 to your computer and use it in GitHub Desktop.
Sample impl of YAP
#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