limes 3.1.0
Composable Calculus Expressions for C++20
Loading...
Searching...
No Matches
integral.hpp
Go to the documentation of this file.
1#pragma once
2
47#include <span>
48#include <string>
49#include <array>
50#include <vector>
51#include <cstddef>
52#include <sstream>
53#include <utility>
54#include "../algorithms/integrators/integrators.hpp"
55#include "../methods/methods.hpp"
56#include "nodes/const.hpp"
57#include "nodes/binary.hpp"
58#include "box_integral.hpp"
59
65namespace limes::expr {
66
67// Forward declarations
68template<typename E, std::size_t Dim, typename Lo, typename Hi>
69struct Integral;
70
71template<typename E>
72struct IntegralBuilder;
73
74template<typename OriginalIntegral, typename Phi, typename Jacobian>
75struct TransformedIntegral;
76
77namespace detail {
78
81template<std::size_t Dim, typename E, typename T>
82auto make_integrand_fn(E const& integrand, std::span<T const> outer_args) {
83 return [&integrand, outer_args](T x) -> T {
84 std::vector<T> full_args;
85 full_args.reserve(E::arity_v);
86
87 std::size_t args_idx = 0;
88 for (std::size_t i = 0; i < E::arity_v; ++i) {
89 if (i == Dim) {
90 full_args.push_back(x);
91 } else if (args_idx < outer_args.size()) {
92 full_args.push_back(outer_args[args_idx++]);
93 } else {
94 full_args.push_back(T{0});
95 }
96 }
97
98 return integrand.eval(std::span<T const>{full_args});
99 };
100}
101
102} // namespace detail
103
104// =============================================================================
105// Type traits for Integral
106// =============================================================================
107
110template<typename E, std::size_t Dim, typename Lo, typename Hi>
111struct is_integral<Integral<E, Dim, Lo, Hi>> : std::true_type {};
112
113template<typename T>
114inline constexpr bool is_integral_v = is_integral<T>::value;
115
117template<typename T>
120
121 constexpr ConstBound() noexcept : value{} {}
122 constexpr explicit ConstBound(T v) noexcept : value{v} {}
123
124 template<typename Args>
125 [[nodiscard]] constexpr T eval(Args const& /*args*/) const noexcept {
126 return value;
127 }
128
129 template<typename Args>
130 [[nodiscard]] [[deprecated("use eval() instead")]]
131 constexpr T evaluate(Args const& args) const noexcept {
132 return eval(args);
133 }
134
135 [[nodiscard]] std::string to_string() const {
136 std::ostringstream oss;
137 oss << value;
138 return oss.str();
139 }
140};
141
143template<typename E>
144struct ExprBound {
146
147 constexpr explicit ExprBound(E e) noexcept : expr{e} {}
148
149 template<typename Args>
150 [[nodiscard]] constexpr auto eval(Args const& args) const {
151 return expr.eval(args);
152 }
153
154 template<typename Args>
155 [[nodiscard]] [[deprecated("use eval() instead")]]
156 constexpr auto evaluate(Args const& args) const {
157 return eval(args);
158 }
159
160 [[nodiscard]] std::string to_string() const {
161 return "(bound " + expr.to_string() + ")";
162 }
163};
164
165// Helper to create bounds
166template<typename T>
167 requires std::is_arithmetic_v<T>
168[[nodiscard]] constexpr auto make_bound(T value) {
169 return ConstBound<T>{value};
170}
171
172template<typename E>
173 requires is_expr_node_v<E>
174[[nodiscard]] constexpr auto make_bound(E expr) {
175 return ExprBound<E>{expr};
176}
177
209template<typename E, std::size_t Dim, typename Lo, typename Hi>
210struct Integral {
211 using value_type = typename E::value_type;
212 using integrand_type = E;
215
216 static constexpr std::size_t dim_v = Dim;
217 static constexpr std::size_t arity_v = (E::arity_v > 0) ? (E::arity_v - 1) : 0;
218
223
224 constexpr Integral(E e, Lo lo, Hi hi, value_type tol = value_type(1e-10)) noexcept
225 : integrand{e}, lower{lo}, upper{hi}, tolerance{tol} {}
226
227 // Evaluate the integral numerically
228 [[nodiscard]] algorithms::integration_result<value_type>
229 eval(std::span<value_type const> args) const {
230 value_type lo = lower.eval(args);
231 value_type hi = upper.eval(args);
232 auto f = detail::make_integrand_fn<Dim>(integrand, args);
233
235 return integrator(f, lo, hi, tolerance);
236 }
237
238 // Convenience: eval with no outer arguments (for fully-integrated expressions)
240 return eval(std::span<value_type const>{});
241 }
242
244 template<typename Method>
245 requires methods::is_integration_method_v<Method>
247 eval(Method const& method, std::span<value_type const> args) const {
248 value_type lo = lower.eval(args);
249 value_type hi = upper.eval(args);
250 auto f = detail::make_integrand_fn<Dim>(integrand, args);
251
252 return method(f, lo, hi);
253 }
254
256 template<typename Method>
257 requires methods::is_integration_method_v<Method>
259 eval(Method const& method) const {
260 return eval(method, std::span<value_type const>{});
261 }
262
263 // Deprecated: use eval() instead
264 [[nodiscard]] [[deprecated("use eval() instead")]]
265 algorithms::integration_result<value_type> evaluate(std::span<value_type const> args) const {
266 return eval(args);
267 }
268
269 // Deprecated: use eval() instead
270 [[nodiscard]] [[deprecated("use eval() instead")]]
274
275 // Chain integration: add another integral layer
276 template<std::size_t NewDim, typename NewLo, typename NewHi>
277 [[nodiscard]] constexpr auto over(NewLo lo, NewHi hi) const {
278 return Integral<Integral, NewDim, decltype(make_bound(lo)), decltype(make_bound(hi))>{
279 *this, make_bound(lo), make_bound(hi), tolerance
280 };
281 }
282
283 // Convenience: over with explicit dimension template parameter
284 // integral(f).over<0>(0.0, 1.0)
285 template<std::size_t NewDim>
286 [[nodiscard]] constexpr auto over(value_type lo, value_type hi) const {
289 };
290 }
291
292 // String representation
293 [[nodiscard]] std::string to_string() const {
294 return "(integral " + integrand.to_string() +
295 " [" + std::to_string(Dim) + " " +
296 lower.to_string() + " " + upper.to_string() + "])";
297 }
298
299 // =========================================================================
300 // Domain Operations
301 // =========================================================================
302
314 template<std::size_t SplitDim = Dim>
315 [[nodiscard]] constexpr auto split(value_type point) const {
316 static_assert(SplitDim == Dim,
317 "split() can only split on the integration dimension");
318
320 using RightIntegral = Integral<E, Dim, ConstBound<value_type>, Hi>;
321
322 auto left = LeftIntegral{integrand, lower, ConstBound<value_type>{point}, tolerance};
323 auto right = RightIntegral{integrand, ConstBound<value_type>{point}, upper, tolerance};
324
325 return std::make_pair(left, right);
326 }
327
338 template<std::size_t D1, std::size_t D2>
339 requires is_integral_v<E>
340 [[nodiscard]] constexpr auto swap() const {
341 // Get the inner integral
342 auto& inner = integrand;
343
344 // For swap to work, we need:
345 // - Outer integral over dimension Dim (this)
346 // - Inner integral over dimension E::dim_v (inner)
347 // We swap them by creating:
348 // - New outer integral over E::dim_v
349 // - New inner integral over Dim
350
351 // Create the swapped structure
352 using InnerIntegrand = typename E::integrand_type;
353 using InnerLo = typename E::lower_bound_type;
354 using InnerHi = typename E::upper_bound_type;
355 constexpr std::size_t InnerDim = E::dim_v;
356
357 // New inner integral: same integrand, but integrated over what was the outer dimension
358 using NewInnerType = Integral<InnerIntegrand, Dim, Lo, Hi>;
359 auto new_inner = NewInnerType{inner.integrand, lower, upper, tolerance};
360
361 // New outer integral: over what was the inner dimension
363 return NewOuterType{new_inner, inner.lower, inner.upper, tolerance};
364 }
365
379 template<typename Phi, typename Jacobian>
380 [[nodiscard]] constexpr auto transform(
381 Phi phi,
382 Jacobian jacobian,
383 value_type new_lower,
384 value_type new_upper
385 ) const {
387 *this, phi, jacobian, new_lower, new_upper, tolerance
388 };
389 }
390
392 [[nodiscard]] constexpr auto with_tolerance(value_type tol) const {
393 return Integral{integrand, lower, upper, tol};
394 }
395};
396
423template<typename E>
425 using value_type = typename E::value_type;
426
429
430 constexpr IntegralBuilder(E e, value_type tol = value_type(1e-10)) noexcept
431 : expr{e}, tolerance{tol} {}
432
433 // over<Dim>(lo, hi): Create an integral over dimension Dim
434 template<std::size_t Dim, typename Lo, typename Hi>
435 [[nodiscard]] constexpr auto over(Lo lo, Hi hi) const {
436 return Integral<E, Dim, decltype(make_bound(lo)), decltype(make_bound(hi))>{
438 };
439 }
440
441 // over(dim, lo, hi): Runtime dimension (limited support)
442 // For now, only supports dimension 0 at runtime
443 template<typename Lo, typename Hi>
444 [[nodiscard]] constexpr auto over(std::size_t dim, Lo lo, Hi hi) const {
445 // Runtime dispatch - support dimensions 0-7
446 if (dim == 0) return over<0>(lo, hi);
447 if (dim == 1) return over<1>(lo, hi);
448 if (dim == 2) return over<2>(lo, hi);
449 if (dim == 3) return over<3>(lo, hi);
450 // Default to dimension 0 for higher dimensions
451 return over<0>(lo, hi);
452 }
453
454 // Set tolerance
455 [[nodiscard]] constexpr IntegralBuilder with_tolerance(value_type tol) const {
456 return IntegralBuilder{expr, tol};
457 }
458
459 // over_box(bounds): Create an N-dimensional box integral for Monte Carlo
460 template<std::size_t Dims>
461 [[nodiscard]] constexpr auto over_box(
462 std::array<std::pair<value_type, value_type>, Dims> bounds
463 ) const {
464 return BoxIntegral<E, Dims>{expr, bounds};
465 }
466
467 // Convenience: over_box with initializer list for 2D
468 [[nodiscard]] constexpr auto over_box(
469 std::pair<value_type, value_type> b0,
470 std::pair<value_type, value_type> b1
471 ) const {
472 return BoxIntegral<E, 2>{expr, {{{b0.first, b0.second}, {b1.first, b1.second}}}};
473 }
474
475 // Convenience: over_box with initializer list for 3D
476 [[nodiscard]] constexpr auto over_box(
477 std::pair<value_type, value_type> b0,
478 std::pair<value_type, value_type> b1,
479 std::pair<value_type, value_type> b2
480 ) const {
481 return BoxIntegral<E, 3>{expr, {{{b0.first, b0.second}, {b1.first, b1.second}, {b2.first, b2.second}}}};
482 }
483};
484
503template<typename E>
504 requires is_expr_node_v<E>
505[[nodiscard]] constexpr auto integral(E expr) {
506 return IntegralBuilder<E>{expr};
507}
508
509// =============================================================================
510// TransformedIntegral: Change of variables for integration
511// =============================================================================
512
543template<typename OriginalIntegral, typename Phi, typename Jacobian>
545 using value_type = typename OriginalIntegral::value_type;
546
547 static constexpr std::size_t arity_v = OriginalIntegral::arity_v;
548
549 OriginalIntegral original;
550 Phi phi; // t → x substitution
551 Jacobian jacobian; // |dφ/dt|
555
557 OriginalIntegral orig,
558 Phi p,
559 Jacobian j,
560 value_type new_lo,
561 value_type new_hi,
562 value_type tol = value_type(1e-10)
563 ) noexcept
564 : original{orig}
565 , phi{p}
566 , jacobian{j}
567 , new_lower{new_lo}
568 , new_upper{new_hi}
569 , tolerance{tol}
570 {}
571
573 [[nodiscard]] algorithms::integration_result<value_type>
574 eval(std::span<value_type const> args) const {
575 constexpr std::size_t dim = OriginalIntegral::dim_v;
576 auto base_fn = detail::make_integrand_fn<dim>(original.integrand, args);
577
578 auto transformed_f = [this, &base_fn](value_type t) -> value_type {
579 value_type x = phi(t);
580 value_type jac = std::abs(jacobian(t));
581 return base_fn(x) * jac;
582 };
583
585 return integrator(transformed_f, new_lower, new_upper, tolerance);
586 }
587
589 return eval(std::span<value_type const>{});
590 }
591
592 // Deprecated: use eval() instead
593 [[nodiscard]] [[deprecated("use eval() instead")]]
594 algorithms::integration_result<value_type> evaluate(std::span<value_type const> args) const {
595 return eval(args);
596 }
597
598 // Deprecated: use eval() instead
599 [[nodiscard]] [[deprecated("use eval() instead")]]
603
604 [[nodiscard]] std::string to_string() const {
605 std::ostringstream oss;
606 oss << "(transformed-integral " << original.to_string()
607 << " [" << new_lower << " " << new_upper << "])";
608 return oss.str();
609 }
610};
611
612// =============================================================================
613// Common Transforms
614// =============================================================================
615
616namespace transforms {
617
619template<typename T>
620struct linear {
621 T a; // scale
622 T b; // shift
623
624 constexpr linear(T scale, T shift) noexcept : a{scale}, b{shift} {}
625
626 [[nodiscard]] constexpr T operator()(T t) const noexcept {
627 return a * t + b;
628 }
629};
630
632template<typename T>
634 T a;
635
636 constexpr explicit linear_jacobian(T scale) noexcept : a{scale} {}
637
638 [[nodiscard]] constexpr T operator()(T /*t*/) const noexcept {
639 return std::abs(a);
640 }
641};
642
644template<typename T>
645struct quadratic {
646 [[nodiscard]] constexpr T operator()(T t) const noexcept {
647 return t * t;
648 }
649};
650
652template<typename T>
654 [[nodiscard]] constexpr T operator()(T t) const noexcept {
655 return T(2) * std::abs(t);
656 }
657};
658
659} // namespace transforms
660
// end of expr_ops group
662
663} // namespace limes::expr
N-dimensional box integration with Monte Carlo methods.
constexpr auto make_bound(T value)
Definition integral.hpp:168
constexpr bool is_integral_v
Definition integral.hpp:114
constexpr auto integral(E expr)
Create an IntegralBuilder for fluent integral construction.
Definition integral.hpp:505
auto make_integrand_fn(E const &integrand, std::span< T const > outer_args)
Definition integral.hpp:82
Expression layer for composable calculus.
Definition analysis.hpp:7
constexpr Var< 0, T > x
Definition var.hpp:46
N-dimensional integral over a rectangular box.
Constant integration bound (e.g., 0.0, 1.0)
Definition integral.hpp:118
constexpr ConstBound() noexcept
Definition integral.hpp:121
constexpr T eval(Args const &) const noexcept
Definition integral.hpp:125
constexpr ConstBound(T v) noexcept
Definition integral.hpp:122
std::string to_string() const
Definition integral.hpp:135
constexpr T evaluate(Args const &args) const noexcept
Definition integral.hpp:131
Expression-valued integration bound (depends on outer variables)
Definition integral.hpp:144
constexpr auto evaluate(Args const &args) const
Definition integral.hpp:156
std::string to_string() const
Definition integral.hpp:160
constexpr ExprBound(E e) noexcept
Definition integral.hpp:147
constexpr auto eval(Args const &args) const
Definition integral.hpp:150
Fluent builder for constructing integrals.
Definition integral.hpp:424
constexpr auto over_box(std::array< std::pair< value_type, value_type >, Dims > bounds) const
Definition integral.hpp:461
constexpr auto over_box(std::pair< value_type, value_type > b0, std::pair< value_type, value_type > b1) const
Definition integral.hpp:468
typename E::value_type value_type
Definition integral.hpp:425
constexpr auto over_box(std::pair< value_type, value_type > b0, std::pair< value_type, value_type > b1, std::pair< value_type, value_type > b2) const
Definition integral.hpp:476
constexpr IntegralBuilder with_tolerance(value_type tol) const
Definition integral.hpp:455
constexpr IntegralBuilder(E e, value_type tol=value_type(1e-10)) noexcept
Definition integral.hpp:430
constexpr auto over(Lo lo, Hi hi) const
Definition integral.hpp:435
constexpr auto over(std::size_t dim, Lo lo, Hi hi) const
Definition integral.hpp:444
Definite integral expression node.
Definition integral.hpp:210
constexpr auto swap() const
Definition integral.hpp:340
constexpr auto transform(Phi phi, Jacobian jacobian, value_type new_lower, value_type new_upper) const
Definition integral.hpp:380
algorithms::integration_result< value_type > evaluate() const
Definition integral.hpp:271
constexpr auto over(value_type lo, value_type hi) const
Definition integral.hpp:286
algorithms::integration_result< value_type > evaluate(std::span< value_type const > args) const
Definition integral.hpp:265
static constexpr std::size_t dim_v
Definition integral.hpp:216
algorithms::integration_result< value_type > eval(Method const &method, std::span< value_type const > args) const
Evaluate the integral using a specific integration method.
Definition integral.hpp:247
std::string to_string() const
Definition integral.hpp:293
constexpr auto with_tolerance(value_type tol) const
Set new tolerance for this integral.
Definition integral.hpp:392
static constexpr std::size_t arity_v
Definition integral.hpp:217
constexpr auto split(value_type point) const
Definition integral.hpp:315
algorithms::integration_result< value_type > eval(std::span< value_type const > args) const
Definition integral.hpp:229
algorithms::integration_result< value_type > eval(Method const &method) const
Evaluate the integral using a specific method (no outer arguments)
Definition integral.hpp:259
typename E::value_type value_type
Definition integral.hpp:211
constexpr auto over(NewLo lo, NewHi hi) const
Definition integral.hpp:277
constexpr Integral(E e, Lo lo, Hi hi, value_type tol=value_type(1e-10)) noexcept
Definition integral.hpp:224
algorithms::integration_result< value_type > eval() const
Definition integral.hpp:239
Integral with change of variables (substitution).
Definition integral.hpp:544
std::string to_string() const
Definition integral.hpp:604
algorithms::integration_result< value_type > eval(std::span< value_type const > args) const
Evaluate the transformed integral numerically.
Definition integral.hpp:574
algorithms::integration_result< value_type > eval() const
Definition integral.hpp:588
static constexpr std::size_t arity_v
Definition integral.hpp:547
constexpr TransformedIntegral(OriginalIntegral orig, Phi p, Jacobian j, value_type new_lo, value_type new_hi, value_type tol=value_type(1e-10)) noexcept
Definition integral.hpp:556
typename OriginalIntegral::value_type value_type
Definition integral.hpp:545
algorithms::integration_result< value_type > evaluate() const
Definition integral.hpp:600
algorithms::integration_result< value_type > evaluate(std::span< value_type const > args) const
Definition integral.hpp:594
Linear transform Jacobian: |dx/dt| = |a|.
Definition integral.hpp:633
constexpr T operator()(T) const noexcept
Definition integral.hpp:638
constexpr linear_jacobian(T scale) noexcept
Definition integral.hpp:636
Linear transform: x = a*t + b.
Definition integral.hpp:620
constexpr linear(T scale, T shift) noexcept
Definition integral.hpp:624
constexpr T operator()(T t) const noexcept
Definition integral.hpp:626
Quadratic transform Jacobian: |dx/dt| = 2|t|.
Definition integral.hpp:653
constexpr T operator()(T t) const noexcept
Definition integral.hpp:654
Quadratic transform: x = t^2 (removes sqrt singularities at x=0)
Definition integral.hpp:645
constexpr T operator()(T t) const noexcept
Definition integral.hpp:646