/******************************************************************************* * * TRIQS: a Toolbox for Research in Interacting Quantum Systems * * Copyright (C) 2012-2013 by O. Parcollet * * TRIQS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * TRIQS. If not, see . * ******************************************************************************/ #ifndef TRIQS_CLEF_CORE_H #define TRIQS_CLEF_CORE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TRIQS_CLEF_MAXNARGS 8 namespace triqs { namespace clef { using ull_t = unsigned long long; namespace tags { struct function_class{}; struct function{}; struct subscript{}; struct terminal{}; struct if_else{}; struct unary_op{}; struct binary_op{}; } // Compute the type to put in the expression tree. // If T is a lvalue reference, pack it into a reference_wrapper, unless force_copy_in_expr::value == true // If T is an rvalue reference, we store it as the type (using move semantics). template struct force_copy_in_expr : std::false_type{}; template struct force_copy_in_expr : force_copy_in_expr{}; template< class T > struct expr_storage_impl {typedef T type;}; template< class T > struct expr_storage_impl : std::conditional::value, typename std::remove_const::type ,std::reference_wrapper>{}; template< class T > struct expr_storage_impl {typedef T type;}; template< class T > struct expr_storage_impl {typedef T type;}; template< class T > struct expr_storage_impl {typedef T type;}; template using expr_storage_t = typename expr_storage_impl::type; // helper type /* --------------------------------------------------------------------------------------------------- * Placeholder and corresponding traits * --------------------------------------------------------------------------------------------------- */ template struct pair; // forward // a placeholder is an empty struct, labelled by an int. template struct placeholder { static_assert( (N>=0) && (N<64) , "Placeholder number limited to [0,63]"); static constexpr int index = N; template pair operator = (RHS && rhs) { return {std::forward(rhs)};} }; // placeholder will always be copied (they are empty anyway). template struct force_copy_in_expr> : std::true_type {}; // represent a couple (placeholder, value). template struct pair { U rhs; static constexpr int p = N; using value_type = typename remove_cv_ref::type; }; // ph_set is a trait that given a pack of type, returns the set of placeholders they contain // it returns a int in binary coding : bit N in the int is 1 iif at least one T is lazy and contains placeholder template struct ph_set; template struct ph_set{static constexpr ull_t value= ph_set::value| ph_set::value;}; template struct ph_set {static constexpr ull_t value= 0;}; template struct ph_set> {static constexpr ull_t value= 1ull< struct ph_set > : ph_set>{}; // in_any_lazy : trait to detect if any of Args is a lazy expression template struct is_any_lazy : std::false_type {}; template struct is_any_lazy > : std::true_type {}; template struct is_any_lazy< T > : std::false_type {}; template struct is_any_lazy< T&& > : is_any_lazy {}; template struct is_any_lazy< T& > : is_any_lazy {}; template struct is_any_lazy< T const > : is_any_lazy {}; template struct is_any_lazy : std::integral_constant::value || is_any_lazy::value> {}; template constexpr bool ClefExpression() { return is_any_lazy::value;} template struct is_clef_expression : is_any_lazy{}; /* --------------------------------------------------------------------------------------------------- * Node of the expression tree * --------------------------------------------------------------------------------------------------- */ template struct expr { // T can be U, U & (a reference or a value). using childs_t = std::tuple; childs_t childs; expr(expr const & x) = default; expr(expr && x) noexcept : childs(std::move(x.childs)) {} // a constructor with the Tag make it unambiguous with other constructors... template expr(Tag, Args&&...args) : childs(std::forward(args)...) {} // [] returns a new lazy expression, with one more layer template expr > operator[](Args && args) const { return {tags::subscript(), *this,std::forward(args)};} // () also ... template< typename... Args > expr...> operator()(Args && ... args) const { return {tags::function(), *this,std::forward(args)...};} // assignement is in general deleted expr & operator= (expr const &) = delete; // no ordinary assignment expr & operator= (expr &&) = default; // move assign ok // however, this is ok in the case f(i,j) = expr, where f is a clef::function template ENABLE_IF(std::is_base_of::type>) operator= (RHS const & rhs) { *this << rhs;} template DISABLE_IF(std::is_base_of::type>) operator= (RHS const & rhs) = delete; }; // set some traits template struct ph_set< expr > : ph_set {}; template struct is_any_lazy< expr >: std::true_type {}; // if we want that subexpression are copied ? template struct force_copy_in_expr< expr > : std::true_type{}; template using expr_node_t = expr...>; /* --------------------------------------------------------------------------------------------------- * The basic operations put in a template.... * --------------------------------------------------------------------------------------------------- */ template struct operation; // a little function to clean the reference_wrapper template U _cl(U && x) { return std::forward(x);} template auto _cl(std::reference_wrapper x) DECL_AND_RETURN(x.get()); /// Terminal template<> struct operation { template L operator()(L&& l) const { return std::forward(l);} }; /// Function call template<> struct operation { template auto operator()(F&& f, Args&&... args) const DECL_AND_RETURN(_cl(std::forward(f))(_cl(std::forward(args))...)); }; /// [ ] Call template<> struct operation { template auto operator()(F&& f, Args&& args) const DECL_AND_RETURN(_cl(std::forward(f))[_cl(std::forward(args))]); }; // all binary operators.... #define TRIQS_CLEF_OPERATION(TAG, OP) \ namespace tags { \ struct TAG : binary_op { \ static const char* name() { return BOOST_PP_STRINGIZE(OP); } \ }; \ } \ template \ std14::enable_if_t::value, expr, expr_storage_t>> operator OP(L&& l, R&& r) { \ return {tags::TAG(), std::forward(l), std::forward(r)}; \ } \ template <> struct operation { \ template \ auto operator()(L&& l, R&& r) const DECL_AND_RETURN(_cl(std::forward(l)) OP _cl(std::forward(r))); \ }; TRIQS_CLEF_OPERATION(plus, +); TRIQS_CLEF_OPERATION(minus, -); TRIQS_CLEF_OPERATION(multiplies, *); TRIQS_CLEF_OPERATION(divides, /); TRIQS_CLEF_OPERATION(greater, >); TRIQS_CLEF_OPERATION(less, <); TRIQS_CLEF_OPERATION(leq, <=); TRIQS_CLEF_OPERATION(geq, >=); TRIQS_CLEF_OPERATION(eq, ==); #undef TRIQS_CLEF_OPERATION // all unary operators.... #define TRIQS_CLEF_OPERATION(TAG, OP) \ namespace tags { \ struct TAG : unary_op { \ static const char* name() { return BOOST_PP_STRINGIZE(OP); } \ }; \ } \ template \ std14::enable_if_t::value, expr>> operator OP(L&& l) { \ return {tags::TAG(), std::forward(l)}; \ } \ template <> struct operation { \ template auto operator()(L&& l) const DECL_AND_RETURN(OP _cl(std::forward(l))); \ }; TRIQS_CLEF_OPERATION(negate, -); TRIQS_CLEF_OPERATION(loginot, !); #undef TRIQS_CLEF_OPERATION /// the only ternary node : expression if template<> struct operation { // A and B MUST be the same template A operator()(C const & c, A const & a, B const & b) const {return _cl(c) ? _cl(a): _cl(b);} //template auto operator()(C const & c, A const & a, B const & b) const DECL_AND_RETURN (_cl(c) ? _cl(a): _cl(b)); }; // operator is : if_else( Condition, A, B) template expr,expr_storage_t,expr_storage_t> if_else(C && c, A && a, B && b) { return {tags::if_else(),std::forward(c),std::forward(a),std::forward(b)};} /* --------------------------------------------------------------------------------------------------- * Evaluation of the expression tree. * --------------------------------------------------------------------------------------------------- */ constexpr bool __or() { return false;} template constexpr bool __or(bool b, B... bs) { return b || __or(bs...); } // Generic case : do nothing (for the leaf of the tree including placeholder) template struct evaluator { static constexpr bool is_lazy = is_any_lazy::value; T const & operator()(T const& k, Pairs const&... pairs) const { return k; } }; // The general eval function for expressions : declaration only template auto eval (T const & ex, Pairs const &... pairs) ->decltype( evaluator()(ex, pairs...)); // placeholder template struct evaluator, pair, Pairs...> { using eval_t = evaluator, Pairs...>; static constexpr bool is_lazy = eval_t::is_lazy; auto operator()(placeholder, pair const&, Pairs const&... pairs) const DECL_AND_RETURN(eval_t()(placeholder(), pairs...)); }; template struct evaluator, pair, Pairs...> { static constexpr bool is_lazy = false; T operator()(placeholder, pair const& p, Pairs const&...) const { return p.rhs; } }; // any object hold by reference wrapper is redirected to the evaluator of the object template struct evaluator, Contexts...> { //using ev_t = evaluator; static constexpr bool is_lazy = false;//ev_t::is_lazy; auto operator()(std::reference_wrapper const& x, Contexts const&... contexts) const DECL_AND_RETURN(eval(x.get(), contexts...)); }; // dispatching the evaluation : if lazy, we make a new clef expression, if not we call the operation template struct op_dispatch; template struct op_dispatch { template expr...> operator()(Args&&... args) const { return {Tag(), std::forward(args)...}; } }; template struct op_dispatch { template auto operator()(Args&&... args) const DECL_AND_RETURN(operation()(std::forward(args)...)); }; // general expr node #ifdef TRIQS_USE_C14_DRAFT__ template struct evaluator, Pairs...> { static constexpr bool is_lazy = __or(evaluator::is_lazy...); auto operator()(expr const& ex, Pairs const&... pairs) const { auto eval_in_context = [&pairs...](auto const& _child) { return eval(_child, pairs...); }; return tuple::apply_compose(op_dispatch{}, eval_in_context, ex.childs); } }; #else // WORKAROUND FOR C++11 compilers template struct evaluator_node_gal; template struct evaluator_node_gal<1, expr, Pairs...> { static constexpr bool is_lazy = __or(evaluator::is_lazy...); auto operator()(expr const& ex, Pairs const&... pairs) const DECL_AND_RETURN(op_dispatch {}(eval(std::get<0>(ex.childs), pairs...))); }; template struct evaluator_node_gal<2, expr, Pairs...> { static constexpr bool is_lazy = __or(evaluator::is_lazy...); auto operator()(expr const& ex, Pairs const&... pairs) const DECL_AND_RETURN(op_dispatch {}(eval(std::get<0>(ex.childs), pairs...), eval(std::get<1>(ex.childs), pairs...))); }; // the general case for more than 2 nodes. I put 1 and 2 nodes apart, just because it is the most frequent // and macros yield more obscure error messages in case of a pb ... #define AUX(z, p, unused) eval(std::get

(ex.childs), pairs...) #define IMPL(z, NN, unused) \ template struct evaluator_node_gal, Pairs...> { \ static constexpr bool is_lazy = __or(evaluator::is_lazy...); \ auto operator()(expr const& ex, Pairs const&... pairs) const \ DECL_AND_RETURN(op_dispatch {}(BOOST_PP_ENUM(NN, AUX, nil))); \ }; BOOST_PP_REPEAT_FROM_TO(3,BOOST_PP_INC(TRIQS_CLEF_MAXNARGS), IMPL, nil); #undef AUX #undef IMPL template struct evaluator, Pairs...> : evaluator_node_gal, Pairs...>{}; #endif // The general eval function for expressions template auto eval(T const& ex, Pairs const&... pairs) DECL_AND_RETURN(evaluator()(ex, pairs...)); /* --------------------------------------------------------------------------------------------------- * Apply a function object to all the leaves of the expression tree * --------------------------------------------------------------------------------------------------- */ template struct apply_on_each_leaf_impl { F f; template std::c14::enable_if_t::value> operator()(T const& ex) { tuple::for_each(ex.childs, *this); } template std::c14::enable_if_t::value> operator()(T const& x) { f(x); } template std::c14::enable_if_t::value> operator()(std::reference_wrapper const& x) { f(x.get()); } }; template void apply_on_each_leaf(F&& f, Expr const& ex) { auto impl = apply_on_each_leaf_impl{std::forward(f)}; impl(ex); } /* --------------------------------------------------------------------------------------------------- * make_function : transform an expression to a function * --------------------------------------------------------------------------------------------------- */ template< typename Expr, int... Is> struct make_fun_impl { Expr ex; // keep a copy of the expression make_fun_impl(Expr const & ex_) : ex(ex_) {} template auto operator()(Args &&... args) const DECL_AND_RETURN( evaluator...>() ( ex, pair{std::forward(args)}...)); }; // values of the ph, excluding the Is ... template struct ph_filter; template struct ph_filter { static constexpr ull_t value = ph_filter::value & (~ (1ull< struct ph_filter { static constexpr ull_t value = x; }; template< typename Expr, int... Is> struct ph_set > { static constexpr ull_t value = ph_filter ::value, Is...>::value;}; template< typename Expr, int... Is> struct is_any_lazy > : std::integral_constant>::value !=0>{}; template< typename Expr, int... Is> struct force_copy_in_expr > : std::true_type{}; template< typename Expr, typename ... Phs> make_fun_impl::type,Phs::index...> make_function(Expr && ex, Phs...) { return {ex}; } namespace result_of { template< typename Expr, typename ... Phs> struct make_function { using type = make_fun_impl::type, Phs::index...>; }; } template< typename Expr, int... Is,typename... Pairs> struct evaluator, Pairs...> { using e_t = evaluator; //using is_lazy = std::integral_constant>::value != ph_set::value>; static constexpr bool is_lazy = (ph_set>::value != ph_set::value); auto operator()(make_fun_impl const & f, Pairs const & ... pairs) const DECL_AND_RETURN( make_function( e_t()(f.ex, pairs...),placeholder()...)); }; template struct ph_list {}; template ph_list var( placeholder ...) { return {};} template auto operator >> (ph_list, Expr const & ex) DECL_AND_RETURN( make_function(ex, placeholder()...)); /* -------------------------------------------------------------------------------------------------- * make_function * x_ >> expression is the same as make_function(expression,x) * --------------------------------------------------------------------------------------------------- */ template make_fun_impl operator >> (placeholder p, Expr&& ex) { return {ex}; } /* --------------------------------------------------------------------------------------------------- * Auto assign for () * --------------------------------------------------------------------------------------------------- */ // by default it is deleted = not implemented : every class has to define it... template void triqs_clef_auto_assign (T,F) = delete; // remove the ref_wrapper, terminal ... template void triqs_clef_auto_assign (std::reference_wrapper R ,F && f) { triqs_clef_auto_assign(R.get(),std::forward(f));} template void triqs_clef_auto_assign (expr const & t,F && f) { triqs_clef_auto_assign(std::get<0>(t.childs),std::forward(f));} // auto assign of an expr ? (for chain calls) : just reuse the same operator template void triqs_clef_auto_assign (expr && ex, RHS const & rhs) { ex << rhs;} template void triqs_clef_auto_assign (expr const & ex, RHS const & rhs) { ex << rhs;} // The case A(x_,y_) = RHS : we form the function (make_function) and call auto_assign (by ADL) template void operator<<(expr...> && ex, RHS && rhs) { triqs_clef_auto_assign(std::get<0>(ex.childs), make_function(std::forward(rhs), placeholder()...)); } template void operator<<(expr...> const & ex, RHS && rhs) { triqs_clef_auto_assign(std::get<0>(ex.childs), make_function(std::forward(rhs), placeholder()...)); } template void operator<<(expr...> & ex, RHS && rhs) { triqs_clef_auto_assign(std::get<0>(ex.childs), make_function(std::forward(rhs), placeholder()...)); } // any other case e.g. f(x_+y_) = RHS etc .... which makes no sense : compiler will stop template void operator<<(expr && ex, RHS && rhs) = delete; template void operator<<(expr const & ex, RHS && rhs) = delete; /* --------------------------------------------------------------------------------------------------- * Auto assign for [] * --------------------------------------------------------------------------------------------------- */ // by default it is deleted = not implemented : every class has to define it... template void triqs_clef_auto_assign_subscript (T,F) = delete; // remove the ref_wrapper, terminal ... template void triqs_clef_auto_assign_subscript (std::reference_wrapper R ,F && f) { triqs_clef_auto_assign_subscript(R.get(),std::forward(f));} template void triqs_clef_auto_assign_subscript (expr const & t,F && f) { triqs_clef_auto_assign_subscript(std::get<0>(t.childs),std::forward(f));} // auto assign of an expr ? (for chain calls) : just reuse the same operator template void triqs_clef_auto_assign_subscript (expr && ex, RHS const & rhs) { ex << rhs;} template void triqs_clef_auto_assign_subscript (expr const & ex, RHS const & rhs) { ex << rhs;} // Same thing for the [ ] template void operator<<(expr...> const & ex, RHS && rhs) { triqs_clef_auto_assign_subscript(std::get<0>(ex.childs), make_function(std::forward(rhs), placeholder()...)); } template void operator<<(expr...> && ex, RHS && rhs) { triqs_clef_auto_assign_subscript(std::get<0>(ex.childs), make_function(std::forward(rhs), placeholder()...)); } template void operator<<(expr && ex, RHS && rhs) = delete; template void operator<<(expr const & ex, RHS && rhs) = delete; /* -------------------------------------------------------------------------------------------------- * Create a terminal node of an object. the from clone version force copying the object * --------------------------------------------------------------------------------------------------- */ // make a node with the ref, unless it is an rvalue (which is moved). template expr > make_expr(T && x){ return {tags::terminal(), std::forward(x)};} // make a node from a copy of the object template expr::type > make_expr_from_clone(T && x){ return {tags::terminal(), std::forward(x)};} /* -------------------------------------------------------------------------------------------------- * Create a call node of an object * The object can be kept as a : a ref, a copy, a view * --------------------------------------------------------------------------------------------------- */ template struct arity { static constexpr int value =-1;}; namespace _result_of { template< typename Obj, typename... Args > struct make_expr_call : std::enable_if< is_any_lazy::value, expr, expr_storage_t ...> > { static_assert (((arity::value==-1) || (arity::value == sizeof...(Args))), "Object called with a wrong number of arguments"); }; } template< typename Obj, typename... Args > typename _result_of::make_expr_call::type make_expr_call(Obj&& obj, Args &&... args) { return {tags::function(),std::forward(obj), std::forward(args)...};} /* -------------------------------------------------------------------------------------------------- * Create a [] call (subscript) node of an object * The object can be kept as a : a ref, a copy, a view * --------------------------------------------------------------------------------------------------- */ namespace _result_of { template< typename Obj, typename Arg> struct make_expr_subscript : std::enable_if< is_any_lazy::value, expr, expr_storage_t> > {}; } template< typename Obj, typename Arg> typename _result_of::make_expr_subscript::type make_expr_subscript(Obj&& obj, Arg && arg) { return {tags::subscript(),std::forward(obj), std::forward(arg)};} /* -------------------------------------------------------------------------------------------------- * function class : stores any expression polymorphically * f(x_,y_ ) = an expression associates this expression dynamically to f, which * can then be used as a std::function of the same signature... * --------------------------------------------------------------------------------------------------- */ template class function; template class function : tags::function_class { using std_function_type = std::function; mutable std::shared_ptr _exp; // CLEAN THIS MUTABLE ? mutable std::shared_ptr < std_function_type > _fnt_ptr; public: function():_fnt_ptr{std::make_shared ()}{} template explicit function(Expr const& _e, X... x) : _exp(std::make_shared(_e)), _fnt_ptr(new std_function_type(make_function(_e, x...))) {} ReturnType operator()(T const &... t) const { return (*_fnt_ptr)(t...);} template< typename... Args> auto operator()( Args&&... args ) const DECL_AND_RETURN(make_expr_call (*this,std::forward(args)...)); template friend void triqs_clef_auto_assign (function const & x, RHS rhs) { * (x._fnt_ptr) = std_function_type (rhs); x._exp = std::make_shared::type> (rhs.ex); } }; template struct force_copy_in_expr > : std::true_type{}; /* -------------------------------------------------------------------------------------------------- * The macro to make any function lazy * TRIQS_CLEF_MAKE_FNT_LAZY (Arity,FunctionName ) : creates a new function in the triqs::lazy namespace * taking expressions (at least one argument has to be an expression) * The lookup happens by ADL, so IT MUST BE USED IN THE triqs::lazy namespace * --------------------------------------------------------------------------------------------------- */ #define TRIQS_CLEF_MAKE_FNT_LAZY(name) \ struct name##_lazy_impl { \ template auto operator()(A&&... a) const DECL_AND_RETURN(name(std::forward(a)...)); \ }; \ template auto name(A&&... a) DECL_AND_RETURN(make_expr_call(name##_lazy_impl(), std::forward(a)...)); #define TRIQS_CLEF_MAKE_FNT_LAZY_BIS(name) \ struct name##_lazy_impl { \ template auto operator()(A&&... a) const -> decltype(name(std::forward(a)...)); \ }; \ template auto name(A&&... a) DECL_AND_RETURN(make_expr_call(name##_lazy_impl(), std::forward(a)...)); \ template auto name##_lazy_impl::operator()(A&&... a) const DECL_AND_RETURN(name(std::forward(a)...)); #define TRIQS_CLEF_EXTEND_FNT_LAZY(FUN, TRAIT) \ template \ std::c14::enable_if_t::value, clef::expr_node_t> FUN(A&& a) { \ return {clef::tags::function{}, clef::FUN##_lazy_impl{}, std::forward(a)}; \ } #define TRIQS_CLEF_IMPLEMENT_LAZY_METHOD(TY, name) \ struct __clef_lazy_method_impl_##TY##_##name { \ template auto operator()(X&& x, A&&... a) const DECL_AND_RETURN(x.name(std::forward(a)...)); \ friend std::ostream& operator<<(std::ostream& out, __clef_lazy_method_impl_##TY##_##name const& x) { \ return out << "apply_method:"<< BOOST_PP_STRINGIZE(name); \ } \ }; \ template \ auto name(A&&... a) const -> typename triqs::clef::_result_of::make_expr_call<__clef_lazy_method_impl_##TY##_##name,const Obj&,A...>::type \ { return make_expr_call(__clef_lazy_method_impl_##TY##_##name{}, *this, std::forward(a)...);} #define TRIQS_CLEF_IMPLEMENT_LAZY_CALL(...) \ template \ auto operator()(Args&&... args) const& DECL_AND_RETURN(make_expr_call(*this, std::forward(args)...)); \ \ template \ auto operator()(Args&&... args) & DECL_AND_RETURN(make_expr_call(*this, std::forward(args)...)); \ \ template \ auto operator()(Args&&... args) && DECL_AND_RETURN(make_expr_call(std::move(*this), std::forward(args)...)); /* -------------------------------------------------------------------------------------------------- * sum of expressions * --------------------------------------------------------------------------------------------------- */ // sum a function f on a domain D, using a simple foreach template auto sum_f_domain_impl(F const& f, D const& d) -> std::c14::enable_if_t::value, decltype(f(*(d.begin())))> { auto res = decltype(f(*(d.begin()))) {}; using p_t = typename std::decay::type; foreach(d, [&res, &f](p_t const& x) { res = res + f(x); }); return res; } TRIQS_CLEF_MAKE_FNT_LAZY(sum_f_domain_impl); // sum( expression, i = domain) template auto sum(Expr const& f, clef::pair const& d) DECL_AND_RETURN(sum_f_domain_impl(make_function(f, clef::placeholder()), d.rhs)); // two or more indices : sum recursively template auto sum(Expr const& f, clef::pair const& d0, clef::pair const& d1, clef::pair const&... d) DECL_AND_RETURN(sum(sum(f, d0), d1, d...)); }} // namespace triqs::clef #endif