limes 3.1.0
Composable Calculus Expressions for C++20
Loading...
Searching...
No Matches
bound.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <span>
4#include <string>
5#include <vector>
6#include <cstddef>
7#include <sstream>
8#include "const.hpp"
9#include "binary.hpp"
10
11namespace limes::expr {
12
13// BoundExpr<E, Dim, BoundValue>: An expression E with dimension Dim bound to a value.
14//
15// When evaluated, injects the bound value at position Dim and uses
16// the remaining arguments for other dimensions.
17//
18// Example:
19// auto f = x * y; // f(x, y), arity = 2
20// auto g = bind<0>(f, 3.0); // g(y) = 3 * y, arity = 1
21// g.eval({4.0}) == 12.0
22//
23template<typename E, std::size_t Dim, typename BoundValue>
24struct BoundExpr {
25 using value_type = typename E::value_type;
26 using expr_type = E;
27 using bound_type = BoundValue;
28
29 static constexpr std::size_t dim_v = Dim;
30 static constexpr std::size_t original_arity_v = E::arity_v;
31 static constexpr std::size_t arity_v = (E::arity_v > 0) ? (E::arity_v - 1) : 0;
32
34 BoundValue bound_value;
35
36 constexpr BoundExpr(E e, BoundValue v) noexcept
37 : expr{e}, bound_value{v} {}
38
39 [[nodiscard]] constexpr value_type eval(std::span<value_type const> args) const {
40 std::vector<value_type> full_args;
41 full_args.reserve(original_arity_v);
42
43 std::size_t args_idx = 0;
44 for (std::size_t i = 0; i < original_arity_v; ++i) {
45 if (i == Dim) {
46 full_args.push_back(get_bound_value());
47 } else if (args_idx < args.size()) {
48 full_args.push_back(args[args_idx++]);
49 } else {
50 full_args.push_back(value_type{0});
51 }
52 }
53
54 return expr.eval(std::span<value_type const>{full_args});
55 }
56
57 [[nodiscard]] constexpr value_type eval() const {
58 return eval(std::span<value_type const>{});
59 }
60
61 [[nodiscard]] [[deprecated("use eval() instead")]]
62 constexpr value_type evaluate(std::span<value_type const> args) const {
63 return eval(args);
64 }
65
66 [[nodiscard]] [[deprecated("use eval() instead")]]
67 constexpr value_type evaluate() const {
68 return eval();
69 }
70
71 // Derivative: map DerivDim to the original dimension, accounting for the bound slot
72 template<std::size_t DerivDim>
73 [[nodiscard]] constexpr auto derivative() const {
74 constexpr std::size_t original_dim = (DerivDim >= Dim) ? (DerivDim + 1) : DerivDim;
75 auto d_expr = expr.template derivative<original_dim>();
76 return BoundExpr<decltype(d_expr), Dim, BoundValue>{d_expr, bound_value};
77 }
78
79 [[nodiscard]] std::string to_string() const {
80 std::ostringstream oss;
81 oss << "(bind " << Dim << " " << get_bound_value() << " " << expr.to_string() << ")";
82 return oss.str();
83 }
84
85private:
86 [[nodiscard]] constexpr value_type get_bound_value() const {
87 if constexpr (std::is_arithmetic_v<BoundValue>) {
88 return static_cast<value_type>(bound_value);
89 } else {
90 return bound_value.value;
91 }
92 }
93};
94
95// bind<Dim>(expr, value): Bind dimension Dim to a constant value
96template<std::size_t Dim, typename E, typename T>
97 requires is_expr_node_v<E>
98[[nodiscard]] constexpr auto bind(E expr, T value) {
99 static_assert(Dim < E::arity_v || E::arity_v == 0,
100 "Cannot bind a dimension that doesn't exist in the expression");
101
102 using VT = typename E::value_type;
103
104 if constexpr (std::is_arithmetic_v<T>) {
105 return BoundExpr<E, Dim, Const<VT>>{expr, Const<VT>{static_cast<VT>(value)}};
106 } else {
107 return BoundExpr<E, Dim, T>{expr, value};
108 }
109}
110
111// bind_all<D1, D2, ...>(expr, v1, v2, ...): Bind multiple dimensions in sequence
112template<std::size_t Dim, std::size_t... Dims, typename E, typename T, typename... Ts>
113 requires is_expr_node_v<E>
114[[nodiscard]] constexpr auto bind_all(E expr, T value, Ts... values) {
115 auto bound_once = bind<Dim>(expr, value);
116 if constexpr (sizeof...(Dims) == 0) {
117 return bound_once;
118 } else {
119 return bind_all<Dims...>(bound_once, values...);
120 }
121}
122
123// partial(expr, value): Bind dimension 0 (curry from left)
124template<typename E, typename T>
125 requires is_expr_node_v<E>
126[[nodiscard]] constexpr auto partial(E expr, T value) {
127 return bind<0>(expr, value);
128}
129
130// partial_right(expr, value): Bind the last dimension (curry from right)
131template<typename E, typename T>
132 requires is_expr_node_v<E>
133[[nodiscard]] constexpr auto partial_right(E expr, T value) {
134 constexpr std::size_t last_dim = (E::arity_v > 0) ? (E::arity_v - 1) : 0;
135 return bind<last_dim>(expr, value);
136}
137
138} // namespace limes::expr
Expression layer for composable calculus.
Definition analysis.hpp:7
constexpr auto bind_all(E expr, T value, Ts... values)
Definition bound.hpp:114
constexpr auto partial_right(E expr, T value)
Definition bound.hpp:133
constexpr auto bind(E expr, T value)
Definition bound.hpp:98
constexpr auto partial(E expr, T value)
Definition bound.hpp:126
static constexpr std::size_t original_arity_v
Definition bound.hpp:30
BoundValue bound_value
Definition bound.hpp:34
constexpr value_type eval() const
Definition bound.hpp:57
constexpr value_type evaluate() const
Definition bound.hpp:67
typename E::value_type value_type
Definition bound.hpp:25
std::string to_string() const
Definition bound.hpp:79
constexpr value_type evaluate(std::span< value_type const > args) const
Definition bound.hpp:62
static constexpr std::size_t arity_v
Definition bound.hpp:31
constexpr value_type eval(std::span< value_type const > args) const
Definition bound.hpp:39
static constexpr std::size_t dim_v
Definition bound.hpp:29
BoundValue bound_type
Definition bound.hpp:27
constexpr auto derivative() const
Definition bound.hpp:73
constexpr BoundExpr(E e, BoundValue v) noexcept
Definition bound.hpp:36