limes 3.1.0
Composable Calculus Expressions for C++20
Loading...
Searching...
No Matches
analysis.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cstddef>
4#include <cstdint>
5#include <type_traits>
6
7namespace limes::expr {
8
9// Forward declarations for node types
10template<typename T> struct Const;
11template<std::size_t N, typename T> struct Var;
12template<typename Op, typename L, typename R> struct Binary;
13template<typename Op, typename E> struct Unary;
14template<typename Tag, typename E> struct UnaryFunc;
15template<typename E, std::size_t Dim, typename Lo, typename Hi> struct Integral;
16template<typename E, std::size_t Dim, typename BoundValue> struct BoundExpr;
17template<typename T> struct ConstBound;
18template<typename E> struct ExprBound;
19
20// =============================================================================
21// variable_set: Compile-time bitset of dimensions an expression depends on
22// =============================================================================
23
26template<typename E, typename = void>
28 static constexpr std::uint64_t value = 0;
29};
30
32template<std::size_t N, typename T>
33struct variable_set<Var<N, T>> {
34 static constexpr std::uint64_t value = (1ULL << N);
35};
36
38template<typename Op, typename L, typename R>
39struct variable_set<Binary<Op, L, R>> {
40 static constexpr std::uint64_t value = variable_set<L>::value | variable_set<R>::value;
41};
42
44template<typename Op, typename E>
45struct variable_set<Unary<Op, E>> {
46 static constexpr std::uint64_t value = variable_set<E>::value;
47};
48
50template<typename Tag, typename E>
51struct variable_set<UnaryFunc<Tag, E>> {
52 static constexpr std::uint64_t value = variable_set<E>::value;
53};
54
56template<typename E>
58 static constexpr std::uint64_t value = variable_set<E>::value;
59};
60
63template<typename E, std::size_t Dim, typename Lo, typename Hi>
64struct variable_set<Integral<E, Dim, Lo, Hi>> {
65 // Remove the integrated dimension from the integrand's dependencies
66 // Add any dependencies from variable bounds
67 static constexpr std::uint64_t integrand_deps = variable_set<E>::value & ~(1ULL << Dim);
68 static constexpr std::uint64_t bound_deps = variable_set<Lo>::value | variable_set<Hi>::value;
69 static constexpr std::uint64_t value = integrand_deps | bound_deps;
70};
71
73template<typename E, std::size_t Dim, typename BoundValue>
74struct variable_set<BoundExpr<E, Dim, BoundValue>> {
75 // Remove the bound dimension from the expression's dependencies
76 static constexpr std::uint64_t value = variable_set<E>::value & ~(1ULL << Dim);
77};
78
79// =============================================================================
80// Convenience aliases and helper functions
81// =============================================================================
82
84template<typename E>
85inline constexpr std::uint64_t variable_set_v = variable_set<E>::value;
86
88template<typename E, std::size_t Dim>
89inline constexpr bool depends_on_v = (variable_set<E>::value & (1ULL << Dim)) != 0;
90
92template<typename E, std::uint64_t Mask>
93inline constexpr bool depends_on_any_v = (variable_set<E>::value & Mask) != 0;
94
96template<typename E, std::uint64_t Mask>
97inline constexpr bool depends_on_all_v = (variable_set<E>::value & Mask) == Mask;
98
100template<typename E>
101inline constexpr bool is_constant_v = (variable_set<E>::value == 0);
102
104template<typename E>
105inline constexpr std::size_t dependency_count_v = []() constexpr {
106 std::uint64_t bits = variable_set<E>::value;
107 std::size_t count = 0;
108 while (bits) {
109 count += bits & 1;
110 bits >>= 1;
111 }
112 return count;
113}();
114
116template<typename E>
117inline constexpr std::size_t max_dimension_v = []() constexpr {
118 std::uint64_t bits = variable_set<E>::value;
119 if (bits == 0) return std::size_t{0};
120 std::size_t max_dim = 0;
121 for (std::size_t i = 0; i < 64; ++i) {
122 if (bits & (1ULL << i)) {
123 max_dim = i;
124 }
125 }
126 return max_dim;
127}();
128
129// =============================================================================
130// Separability Detection
131// =============================================================================
132
133// Forward declarations for operation tags (defined in nodes/binary.hpp)
134struct Add;
135struct Sub;
136struct Mul;
137
138namespace detail {
139
141template<typename E, std::uint64_t AllowedMask>
142inline constexpr bool depends_only_on_v = (variable_set<E>::value & ~AllowedMask) == 0;
143
145template<typename E, std::size_t D1, std::size_t D2, typename = void>
146struct is_separable_impl : std::false_type {};
147
149template<typename T, std::size_t D1, std::size_t D2>
150struct is_separable_impl<Const<T>, D1, D2> : std::true_type {};
151
154template<std::size_t N, typename T, std::size_t D1, std::size_t D2>
155struct is_separable_impl<Var<N, T>, D1, D2>
156 : std::bool_constant<(N == D1) || (N == D2)> {};
157
160template<typename L, typename R, std::size_t D1, std::size_t D2>
161struct is_separable_impl<Binary<Mul, L, R>, D1, D2> {
162 static constexpr std::uint64_t mask_d1 = 1ULL << D1;
163 static constexpr std::uint64_t mask_d2 = 1ULL << D2;
164
165 // Check if L depends only on D1 and R depends only on D2
166 static constexpr bool lr_separable =
167 depends_only_on_v<L, mask_d1> && depends_only_on_v<R, mask_d2>;
168
169 // Check if L depends only on D2 and R depends only on D1
170 static constexpr bool rl_separable =
171 depends_only_on_v<L, mask_d2> && depends_only_on_v<R, mask_d1>;
172
173 static constexpr bool value = lr_separable || rl_separable;
174};
175
178template<typename L, typename R, std::size_t D1, std::size_t D2>
179struct is_separable_impl<Binary<Add, L, R>, D1, D2>
180 : std::bool_constant<
181 is_separable_impl<L, D1, D2>::value &&
182 is_separable_impl<R, D1, D2>::value
183 > {};
184
185template<typename L, typename R, std::size_t D1, std::size_t D2>
186struct is_separable_impl<Binary<Sub, L, R>, D1, D2>
187 : std::bool_constant<
188 is_separable_impl<L, D1, D2>::value &&
189 is_separable_impl<R, D1, D2>::value
190 > {};
191
194template<typename E, std::size_t D1, std::size_t D2>
195inline constexpr bool single_child_separable_v =
196 depends_only_on_v<E, (1ULL << D1)> || depends_only_on_v<E, (1ULL << D2)>;
197
198template<typename Op, typename E, std::size_t D1, std::size_t D2>
199struct is_separable_impl<Unary<Op, E>, D1, D2>
200 : std::bool_constant<single_child_separable_v<Unary<Op, E>, D1, D2>> {};
201
202template<typename Tag, typename E, std::size_t D1, std::size_t D2>
203struct is_separable_impl<UnaryFunc<Tag, E>, D1, D2>
204 : std::bool_constant<single_child_separable_v<UnaryFunc<Tag, E>, D1, D2>> {};
205
206} // namespace detail
207
210template<typename E, std::size_t D1, std::size_t D2>
211struct is_separable : detail::is_separable_impl<E, D1, D2> {};
212
213template<typename E, std::size_t D1, std::size_t D2>
215
216// =============================================================================
217// Separation: Extract g and h from f(x,y) = g(x) * h(y)
218// =============================================================================
219
220namespace detail {
221
224template<typename E, std::size_t TargetDim, std::size_t OtherDim>
225struct extract_factor;
226
228template<typename T, std::size_t TargetDim, std::size_t OtherDim>
229struct extract_factor<Const<T>, TargetDim, OtherDim> {
230 using type = Const<T>;
231 static constexpr auto extract(Const<T> const& c) { return c; }
232};
233
235template<std::size_t N, typename T, std::size_t TargetDim, std::size_t OtherDim>
236struct extract_factor<Var<N, T>, TargetDim, OtherDim> {
237 using type = std::conditional_t<(N == TargetDim), Var<N, T>, Const<T>>;
238 static constexpr auto extract(Var<N, T> const& v) {
239 if constexpr (N == TargetDim) {
240 return v;
241 } else {
242 return Const<T>{T(1)};
243 }
244 }
245};
246
248template<typename L, typename R, std::size_t TargetDim, std::size_t OtherDim>
249struct extract_factor<Binary<Mul, L, R>, TargetDim, OtherDim> {
250 static constexpr std::uint64_t target_mask = 1ULL << TargetDim;
251
252 // If L depends only on TargetDim, L is our factor
253 // If R depends only on TargetDim, R is our factor
254 static constexpr bool l_is_target = depends_only_on_v<L, target_mask>;
255 static constexpr bool r_is_target = depends_only_on_v<R, target_mask>;
256
257 using type = std::conditional_t<l_is_target, L, R>;
258
259 static constexpr auto extract(Binary<Mul, L, R> const& expr) {
260 if constexpr (l_is_target) {
261 return expr.left;
262 } else {
263 return expr.right;
264 }
265 }
266};
267
270template<typename Expr, std::size_t TargetDim, std::size_t OtherDim>
271 requires requires { typename Expr::value_type; }
272struct extract_single_child_factor {
273 static constexpr bool is_target = depends_only_on_v<Expr, (1ULL << TargetDim)>;
274
275 using value_type = typename Expr::value_type;
276 using type = std::conditional_t<is_target, Expr, Const<value_type>>;
277
278 static constexpr auto extract(Expr const& expr) {
279 if constexpr (is_target) {
280 return expr;
281 } else {
282 return Const<value_type>{value_type(1)};
283 }
284 }
285};
286
287template<typename Op, typename E, std::size_t TargetDim, std::size_t OtherDim>
288struct extract_factor<Unary<Op, E>, TargetDim, OtherDim>
289 : extract_single_child_factor<Unary<Op, E>, TargetDim, OtherDim> {};
290
291template<typename Tag, typename E, std::size_t TargetDim, std::size_t OtherDim>
292struct extract_factor<UnaryFunc<Tag, E>, TargetDim, OtherDim>
293 : extract_single_child_factor<UnaryFunc<Tag, E>, TargetDim, OtherDim> {};
294
295} // namespace detail
296
301template<std::size_t D1, std::size_t D2, typename E>
302constexpr auto separate(E const& expr) {
303 static_assert(is_separable_v<E, D1, D2>,
304 "Expression is not separable with respect to the given dimensions");
305
306 auto g = detail::extract_factor<E, D1, D2>::extract(expr);
307 auto h = detail::extract_factor<E, D2, D1>::extract(expr);
308
309 return std::make_pair(g, h);
310}
311
312} // namespace limes::expr
constexpr bool depends_only_on_v
Helper to check if an expression depends only on certain dimensions.
Definition analysis.hpp:142
constexpr bool single_child_separable_v
Definition analysis.hpp:195
Expression layer for composable calculus.
Definition analysis.hpp:7
constexpr std::size_t max_dimension_v
Get the maximum dimension index an expression depends on (0 if constant)
Definition analysis.hpp:117
constexpr std::size_t dependency_count_v
Count the number of dimensions an expression depends on.
Definition analysis.hpp:105
constexpr bool depends_on_any_v
Check if an expression depends on any of the given dimensions (mask)
Definition analysis.hpp:93
constexpr bool is_constant_v
Check if an expression is constant (depends on no variables)
Definition analysis.hpp:101
constexpr bool is_separable_v
Definition analysis.hpp:214
constexpr bool depends_on_all_v
Check if an expression depends on all of the given dimensions (mask)
Definition analysis.hpp:97
constexpr std::uint64_t variable_set_v
Helper variable template for cleaner syntax.
Definition analysis.hpp:85
constexpr auto separate(E const &expr)
Definition analysis.hpp:302
constexpr bool depends_on_v
Check if an expression depends on a specific dimension.
Definition analysis.hpp:89
Constant integration bound (e.g., 0.0, 1.0)
Definition integral.hpp:118
Expression-valued integration bound (depends on outer variables)
Definition integral.hpp:144
Definite integral expression node.
Definition integral.hpp:210
static constexpr std::uint64_t value
Definition analysis.hpp:28