limes 3.1.0
Composable Calculus Expressions for C++20
Loading...
Searching...
No Matches
sum_product.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <span>
4#include <string>
5#include <cstddef>
6#include <vector>
7#include <sstream>
8#include "binary.hpp"
9
10namespace limes::expr {
11
12namespace detail {
13
14// Build an extended argument vector with a placeholder inserted at IndexDim
15template<typename T>
16std::vector<T> make_extended_args(std::span<T const> args, std::size_t index_dim) {
17 std::vector<T> ext_args(args.begin(), args.end());
18 if (ext_args.size() <= index_dim) {
19 ext_args.resize(index_dim + 1);
20 } else {
21 ext_args.insert(ext_args.begin() + static_cast<std::ptrdiff_t>(index_dim), T(0));
22 }
23 return ext_args;
24}
25
26} // namespace detail
27
28// FiniteSum<Expr, IndexDim>: Sum over integer index, i.e. sum(i=lo..hi) body(x, i)
29template<typename Expr, std::size_t IndexDim>
30struct FiniteSum {
31 using value_type = typename Expr::value_type;
32 using body_type = Expr;
33
34 static constexpr std::size_t arity_v =
35 (Expr::arity_v > IndexDim) ? (Expr::arity_v - 1) : Expr::arity_v;
36
37 Expr body;
38 int lo;
39 int hi;
40
41 constexpr FiniteSum(Expr b, int l, int h) noexcept : body{b}, lo{l}, hi{h} {}
42
43 [[nodiscard]] constexpr value_type eval(std::span<value_type const> args) const {
45 auto ext_args = detail::make_extended_args(args, IndexDim);
46
47 for (int i = lo; i <= hi; ++i) {
48 ext_args[IndexDim] = static_cast<value_type>(i);
49 sum += body.eval(std::span<value_type const>(ext_args));
50 }
51 return sum;
52 }
53
54 [[nodiscard]] [[deprecated("use eval() instead")]]
55 constexpr value_type evaluate(std::span<value_type const> args) const {
56 return eval(args);
57 }
58
59 // d/dx[sum f(x,i)] = sum df/dx(x,i)
60 template<std::size_t Dim>
61 [[nodiscard]] constexpr auto derivative() const {
62 constexpr std::size_t AdjustedDim = (Dim >= IndexDim) ? (Dim + 1) : Dim;
63 auto dbody = body.template derivative<AdjustedDim>();
64 return FiniteSum<decltype(dbody), IndexDim>{dbody, lo, hi};
65 }
66
67 [[nodiscard]] std::string to_string() const {
68 std::ostringstream oss;
69 oss << "(sum[i" << IndexDim << "=" << lo << ".." << hi << "] " << body.to_string() << ")";
70 return oss.str();
71 }
72};
73
74// FiniteProduct<Expr, IndexDim>: Product over integer index, i.e. prod(i=lo..hi) body(x, i)
75template<typename Expr, std::size_t IndexDim>
77 using value_type = typename Expr::value_type;
78 using body_type = Expr;
79
80 static constexpr std::size_t arity_v =
81 (Expr::arity_v > IndexDim) ? (Expr::arity_v - 1) : Expr::arity_v;
82
83 Expr body;
84 int lo;
85 int hi;
86
87 constexpr FiniteProduct(Expr b, int l, int h) noexcept : body{b}, lo{l}, hi{h} {}
88
89 [[nodiscard]] constexpr value_type eval(std::span<value_type const> args) const {
90 value_type prod = value_type(1);
91 auto ext_args = detail::make_extended_args(args, IndexDim);
92
93 for (int i = lo; i <= hi; ++i) {
94 ext_args[IndexDim] = static_cast<value_type>(i);
95 prod *= body.eval(std::span<value_type const>(ext_args));
96 }
97 return prod;
98 }
99
100 [[nodiscard]] [[deprecated("use eval() instead")]]
101 constexpr value_type evaluate(std::span<value_type const> args) const {
102 return eval(args);
103 }
104
105 // Logarithmic derivative: d/dx[prod f_i] = (prod f_i) * sum(f_i'/f_i)
106 template<std::size_t Dim>
107 [[nodiscard]] constexpr auto derivative() const {
108 constexpr std::size_t AdjustedDim = (Dim >= IndexDim) ? (Dim + 1) : Dim;
109 auto dbody = body.template derivative<AdjustedDim>();
110 auto term = dbody / body;
111 auto sum_of_terms = FiniteSum<decltype(term), IndexDim>{term, lo, hi};
112 return (*this) * sum_of_terms;
113 }
114
115 [[nodiscard]] std::string to_string() const {
116 std::ostringstream oss;
117 oss << "(prod[i" << IndexDim << "=" << lo << ".." << hi << "] " << body.to_string() << ")";
118 return oss.str();
119 }
120};
121
122// Type traits
123
124template<typename T>
125struct is_finite_sum : std::false_type {};
126
127template<typename E, std::size_t I>
128struct is_finite_sum<FiniteSum<E, I>> : std::true_type {};
129
130template<typename T>
132
133template<typename T>
134struct is_finite_product : std::false_type {};
135
136template<typename E, std::size_t I>
137struct is_finite_product<FiniteProduct<E, I>> : std::true_type {};
138
139template<typename T>
141
142// Factory functions
143
144template<std::size_t IndexDim, typename E>
145 requires is_expr_node_v<E>
146[[nodiscard]] constexpr auto sum(E body, int lo, int hi) {
147 return FiniteSum<E, IndexDim>{body, lo, hi};
148}
149
150template<std::size_t IndexDim, typename E>
151 requires is_expr_node_v<E>
152[[nodiscard]] constexpr auto product(E body, int lo, int hi) {
153 return FiniteProduct<E, IndexDim>{body, lo, hi};
154}
155
156} // namespace limes::expr
std::vector< T > make_extended_args(std::span< T const > args, std::size_t index_dim)
Expression layer for composable calculus.
Definition analysis.hpp:7
constexpr auto sum(E body, int lo, int hi)
constexpr bool is_finite_sum_v
constexpr auto product(E body, int lo, int hi)
constexpr bool is_finite_product_v
typename Expr::value_type value_type
constexpr auto derivative() const
constexpr value_type eval(std::span< value_type const > args) const
constexpr value_type evaluate(std::span< value_type const > args) const
static constexpr std::size_t arity_v
std::string to_string() const
constexpr FiniteProduct(Expr b, int l, int h) noexcept
std::string to_string() const
constexpr value_type eval(std::span< value_type const > args) const
typename Expr::value_type value_type
constexpr auto derivative() const
constexpr value_type evaluate(std::span< value_type const > args) const
static constexpr std::size_t arity_v
constexpr FiniteSum(Expr b, int l, int h) noexcept