Skip to content

Deep partial evaluation for CLEF expression #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: unstable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion c++/nda/clef/expression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,27 @@ namespace nda::clef {
* @return An nda::clef::expr object with the nda::clef::tags::function tag containing the current expression node
* as the first child node and the other arguments as the remaining child nodes.
*/
#ifdef __cpp_explicit_this_parameter
template <typename Self, typename... Args>
auto operator()(this Self &&self, Args &&...args) {
return expr<tags::function, expr, expr_storage_t<Args>...>{tags::function(), std::forward<Self>(self), std::forward<Args>(args)...};
}
#else
template <typename... Args>
auto operator()(Args &&...args) const & {
return expr<tags::function, expr, expr_storage_t<Args>...>{tags::function(), *this, std::forward<Args>(args)...};
}

template <typename... Args>
auto operator()(Args &&...args) const {
auto operator()(Args &&...args) & {
return expr<tags::function, expr, expr_storage_t<Args>...>{tags::function(), *this, std::forward<Args>(args)...};
}

template <typename... Args>
auto operator()(Args &&...args) && {
return expr<tags::function, expr, expr_storage_t<Args>...>{tags::function(), std::move(*this), std::forward<Args>(args)...};
}
#endif
};

namespace detail {
Expand Down
34 changes: 31 additions & 3 deletions c++/nda/clef/operation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@

namespace nda::clef {

/// During a partial evaluation of an expression, the
// function nodes can be evaluated in 2 ways :
// - default : the function is evaluated iif all the arguments are non lazy
// otherwise the node is kept, with its children replaced by evaluation
// i.e. the function is NOT called.
// - if true : the function is CALLED with all the arguments, lazy of not.
// It is not the default, as the function/object must be properly
// implemented, by MOVING the argument in a new function node
// with make_expr_call
template <typename F>
constexpr bool supports_partial_eval_of_calls = false;

/// Same as supports_partial_eval_of_calls but for the subscript operator
template <typename F>
constexpr bool supports_partial_eval_of_subscript = false;

/**
* @addtogroup clef_expr
* @{
Expand Down Expand Up @@ -81,7 +97,9 @@ namespace nda::clef {
* @return Result of the function call.
*/
template <typename F, typename... Args>
FORCEINLINE decltype(auto) operator()(F &&f, Args &&...args) const {
FORCEINLINE auto operator()(F &&f, Args &&...args)
-> decltype(detail::fget(std::forward<F>(f))(detail::fget(std::forward<Args>(args))...)) const {
// trailing decltype is necessary for requires later in operation<Tag>
return detail::fget(std::forward<F>(f))(detail::fget(std::forward<Args>(args))...);
}
};
Expand All @@ -99,7 +117,8 @@ namespace nda::clef {
* @return Result of the subscript operation.
*/
template <typename F, typename... Args>
FORCEINLINE decltype(auto) operator()(F &&f, Args &&...args) const {
FORCEINLINE auto operator()(F &&f, Args &&...args)
-> decltype(detail::fget(std::forward<F>(f)).operator[](detail::fget(std::forward<Args>(args))...)) const {
// directly calling [args...] breaks clang
return detail::fget(std::forward<F>(f)).operator[](detail::fget(std::forward<Args>(args))...);
}
Expand Down Expand Up @@ -224,9 +243,18 @@ namespace nda::clef {
* @param args Operands.
* @return An nda::clef::expr for the given operation and operands.
*/

template <typename Tag, typename... Args>
FORCEINLINE auto op_dispatch(std::true_type, Args &&...args) {
return expr<Tag, expr_storage_t<Args>...>{Tag(), std::forward<Args>(args)...};
using Arg0 = std::decay_t<std::tuple_element_t<0, std::tuple<Args...>>>;
if constexpr (not(std::is_same_v<Tag, tags::function> and not supports_partial_eval_of_calls<Arg0>) and //
not(std::is_same_v<Tag, tags::subscript> and not supports_partial_eval_of_subscript<Arg0>) //and //
//requires { operation<Tag>()(std::forward<Args>(args)...); }
) {
return operation<Tag>()(std::forward<Args>(args)...);
} else {
return expr<Tag, expr_storage_t<Args>...>{Tag(), std::forward<Args>(args)...};
}
}

/**
Expand Down
50 changes: 50 additions & 0 deletions test/c++/nda_clef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <array>
#include <functional>
#include <iostream>
#include <sstream>
#include <type_traits>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -474,3 +475,52 @@ TEST_F(CLEF, SumExpressionOverDomain) {
EXPECT_EQ(dom1.size(), 3);
EXPECT_EQ(clef::sum(ex3, x0_ = std::vector{1, 2, 3}, x1_ = std::vector{4, 5, 6}), 5 + 2 * 6 + 3 * 7 + 2 * 8 + 9);
}

// ========= Deep Evaluation of Function Calls ==========
int f1(int x) {
//std::cerr << "Eval f1 " << x << std::endl;
return 100 * x;
}
CLEF_MAKE_FNT_LAZY(f1);

struct _f3 {
auto operator()(auto x, auto y) const {
if constexpr (nda::clef::is_lazy<decltype(x)>) {
if constexpr (nda::clef::is_lazy<decltype(y)>) {
return make_expr_call(*this, x, y);
} else {
// std::cerr << "Eval f3 ONE LAZY " << x << " " << y << std::endl;
return f1(x) + auto{y};
}
} else {
// std::cerr << "Eval f3 " << x << " " << y << std::endl;
return 10 * x + y;
}
}
};
template <>
constexpr bool nda::clef::supports_partial_eval_of_calls<_f3> = true;

static constexpr _f3 f3{};
std::ostream &operator<<(std::ostream &out, _f3) { return out << "f3"; }

TEST_F(CLEF, DeepEval1) {
nda::clef::placeholder<0> x_;
nda::clef::placeholder<1> y_;
nda::clef::placeholder<2> z_;
auto ex = x_ + (10 * y_ + 100 * z_);
auto ev = eval(ex, y_ = 20, z_ = 30);
std::stringstream s1, s2;
s1 << ev;
s2 << x_ + 3200;
EXPECT_EQ(s1.str(), s2.str());
EXPECT_EQ(eval(ev, x_ = 1), 1 + 10 * 20 + 100 * 30);
}

TEST_F(CLEF, DeepEvalFntCall) {
nda::clef::placeholder<0> x_;
nda::clef::placeholder<1> y_;
auto ex = x_ + f3(x_, y_);
auto ev = eval(ex, y_ = 20);
EXPECT_EQ(eval(ev, x_ = 1), 1 + f1(1) + 20);
}
Loading