limes 3.1.0
Composable Calculus Expressions for C++20
Loading...
Searching...
No Matches
primitives.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <span>
4#include <string>
5#include <string_view>
6#include <cstddef>
7#include <cmath>
8#include "binary.hpp"
9#include "unary.hpp"
10
11namespace limes::expr {
12
13// Primitive function tags
14struct ExpTag {};
15struct LogTag {};
16struct SinTag {};
17struct CosTag {};
18struct SqrtTag {};
19struct AbsTag {};
20struct TanTag {};
21struct SinhTag {};
22struct CoshTag {};
23struct TanhTag {};
24struct AsinTag {};
25struct AcosTag {};
26struct AtanTag {};
27struct AsinhTag {};
28struct AcoshTag {};
29struct AtanhTag {};
30
31namespace detail {
32
33// Map function tag to its display name
34template<typename Tag>
35constexpr std::string_view tag_name() {
36 if constexpr (std::is_same_v<Tag, ExpTag>) return "exp";
37 else if constexpr (std::is_same_v<Tag, LogTag>) return "log";
38 else if constexpr (std::is_same_v<Tag, SinTag>) return "sin";
39 else if constexpr (std::is_same_v<Tag, CosTag>) return "cos";
40 else if constexpr (std::is_same_v<Tag, SqrtTag>) return "sqrt";
41 else if constexpr (std::is_same_v<Tag, AbsTag>) return "abs";
42 else if constexpr (std::is_same_v<Tag, TanTag>) return "tan";
43 else if constexpr (std::is_same_v<Tag, SinhTag>) return "sinh";
44 else if constexpr (std::is_same_v<Tag, CoshTag>) return "cosh";
45 else if constexpr (std::is_same_v<Tag, TanhTag>) return "tanh";
46 else if constexpr (std::is_same_v<Tag, AsinTag>) return "asin";
47 else if constexpr (std::is_same_v<Tag, AcosTag>) return "acos";
48 else if constexpr (std::is_same_v<Tag, AtanTag>) return "atan";
49 else if constexpr (std::is_same_v<Tag, AsinhTag>) return "asinh";
50 else if constexpr (std::is_same_v<Tag, AcoshTag>) return "acosh";
51 else if constexpr (std::is_same_v<Tag, AtanhTag>) return "atanh";
52}
53
54} // namespace detail
55
56// UnaryFunc<Tag, E>: A unary function applied to a child expression.
57// This is the expression node for primitives like exp, sin, cos, sqrt, etc.
58template<typename Tag, typename E>
59struct UnaryFunc {
60 using value_type = typename E::value_type;
61 using tag_type = Tag;
62 using child_type = E;
63
64 static constexpr std::size_t arity_v = E::arity_v;
65
67
68 constexpr explicit UnaryFunc(E c) noexcept : child{c} {}
69
70 [[nodiscard]] constexpr value_type eval(std::span<value_type const> args) const {
71 value_type c_val = child.eval(args);
72
73 if constexpr (std::is_same_v<Tag, ExpTag>) return std::exp(c_val);
74 else if constexpr (std::is_same_v<Tag, LogTag>) return std::log(c_val);
75 else if constexpr (std::is_same_v<Tag, SinTag>) return std::sin(c_val);
76 else if constexpr (std::is_same_v<Tag, CosTag>) return std::cos(c_val);
77 else if constexpr (std::is_same_v<Tag, SqrtTag>) return std::sqrt(c_val);
78 else if constexpr (std::is_same_v<Tag, AbsTag>) return std::abs(c_val);
79 else if constexpr (std::is_same_v<Tag, TanTag>) return std::tan(c_val);
80 else if constexpr (std::is_same_v<Tag, SinhTag>) return std::sinh(c_val);
81 else if constexpr (std::is_same_v<Tag, CoshTag>) return std::cosh(c_val);
82 else if constexpr (std::is_same_v<Tag, TanhTag>) return std::tanh(c_val);
83 else if constexpr (std::is_same_v<Tag, AsinTag>) return std::asin(c_val);
84 else if constexpr (std::is_same_v<Tag, AcosTag>) return std::acos(c_val);
85 else if constexpr (std::is_same_v<Tag, AtanTag>) return std::atan(c_val);
86 else if constexpr (std::is_same_v<Tag, AsinhTag>) return std::asinh(c_val);
87 else if constexpr (std::is_same_v<Tag, AcoshTag>) return std::acosh(c_val);
88 else if constexpr (std::is_same_v<Tag, AtanhTag>) return std::atanh(c_val);
89 }
90
91 [[nodiscard]] [[deprecated("use eval() instead")]]
92 constexpr value_type evaluate(std::span<value_type const> args) const {
93 return eval(args);
94 }
95
96 // Chain rule: d/dx[f(u)] = f'(u) * du/dx
97 template<std::size_t Dim>
98 [[nodiscard]] constexpr auto derivative() const {
99 auto dc = child.template derivative<Dim>();
100
101 if constexpr (std::is_same_v<Tag, ExpTag>) {
102 // d[exp(u)] = exp(u) * du
103 return (*this) * dc;
104 } else if constexpr (std::is_same_v<Tag, LogTag>) {
105 // d[log(u)] = du / u
106 return (One<value_type>{} / child) * dc;
107 } else if constexpr (std::is_same_v<Tag, SinTag>) {
108 // d[sin(u)] = cos(u) * du
109 return UnaryFunc<CosTag, E>{child} * dc;
110 } else if constexpr (std::is_same_v<Tag, CosTag>) {
111 // d[cos(u)] = -sin(u) * du
112 return -UnaryFunc<SinTag, E>{child} * dc;
113 } else if constexpr (std::is_same_v<Tag, SqrtTag>) {
114 // d[sqrt(u)] = du / (2 * sqrt(u))
116 return (One<value_type>{} / two_sqrt) * dc;
117 } else if constexpr (std::is_same_v<Tag, AbsTag>) {
118 // d[|u|] = sign(u) * du, where sign(u) = u / |u|
119 return (child / UnaryFunc<AbsTag, E>{child}) * dc;
120 } else if constexpr (std::is_same_v<Tag, TanTag>) {
121 // d[tan(u)] = sec^2(u) * du = du / cos^2(u)
122 auto cos_child = UnaryFunc<CosTag, E>{child};
123 return (One<value_type>{} / (cos_child * cos_child)) * dc;
124 } else if constexpr (std::is_same_v<Tag, SinhTag>) {
125 // d[sinh(u)] = cosh(u) * du
126 return UnaryFunc<CoshTag, E>{child} * dc;
127 } else if constexpr (std::is_same_v<Tag, CoshTag>) {
128 // d[cosh(u)] = sinh(u) * du
129 return UnaryFunc<SinhTag, E>{child} * dc;
130 } else if constexpr (std::is_same_v<Tag, TanhTag>) {
131 // d[tanh(u)] = (1 - tanh^2(u)) * du
132 auto tanh_child = *this;
133 return (One<value_type>{} - tanh_child * tanh_child) * dc;
134 } else if constexpr (std::is_same_v<Tag, AsinTag>) {
135 // d[asin(u)] = du / sqrt(1 - u^2)
136 auto one = One<value_type>{};
137 auto denom = UnaryFunc<SqrtTag, decltype(one - child * child)>{one - child * child};
138 return (one / denom) * dc;
139 } else if constexpr (std::is_same_v<Tag, AcosTag>) {
140 // d[acos(u)] = -du / sqrt(1 - u^2)
141 auto one = One<value_type>{};
142 auto denom = UnaryFunc<SqrtTag, decltype(one - child * child)>{one - child * child};
143 return -(one / denom) * dc;
144 } else if constexpr (std::is_same_v<Tag, AtanTag>) {
145 // d[atan(u)] = du / (1 + u^2)
146 auto one = One<value_type>{};
147 return (one / (one + child * child)) * dc;
148 } else if constexpr (std::is_same_v<Tag, AsinhTag>) {
149 // d[asinh(u)] = du / sqrt(1 + u^2)
150 auto one = One<value_type>{};
151 auto denom = UnaryFunc<SqrtTag, decltype(one + child * child)>{one + child * child};
152 return (one / denom) * dc;
153 } else if constexpr (std::is_same_v<Tag, AcoshTag>) {
154 // d[acosh(u)] = du / sqrt(u^2 - 1)
155 auto one = One<value_type>{};
156 auto denom = UnaryFunc<SqrtTag, decltype(child * child - one)>{child * child - one};
157 return (one / denom) * dc;
158 } else if constexpr (std::is_same_v<Tag, AtanhTag>) {
159 // d[atanh(u)] = du / (1 - u^2)
160 auto one = One<value_type>{};
161 return (one / (one - child * child)) * dc;
162 }
163 }
164
165 [[nodiscard]] std::string to_string() const {
166 return "(" + std::string(detail::tag_name<Tag>()) + " " + child.to_string() + ")";
167 }
168};
169
170// Factory functions for creating primitive function expressions
171
172template<typename E>
173 requires is_expr_node_v<E>
174[[nodiscard]] constexpr auto exp(E e) { return UnaryFunc<ExpTag, E>{e}; }
175
176template<typename E>
177 requires is_expr_node_v<E>
178[[nodiscard]] constexpr auto log(E e) { return UnaryFunc<LogTag, E>{e}; }
179
180template<typename E>
181 requires is_expr_node_v<E>
182[[nodiscard]] constexpr auto sin(E e) { return UnaryFunc<SinTag, E>{e}; }
183
184template<typename E>
185 requires is_expr_node_v<E>
186[[nodiscard]] constexpr auto cos(E e) { return UnaryFunc<CosTag, E>{e}; }
187
188template<typename E>
189 requires is_expr_node_v<E>
190[[nodiscard]] constexpr auto sqrt(E e) { return UnaryFunc<SqrtTag, E>{e}; }
191
192template<typename E>
193 requires is_expr_node_v<E>
194[[nodiscard]] constexpr auto abs(E e) { return UnaryFunc<AbsTag, E>{e}; }
195
196template<typename E>
197 requires is_expr_node_v<E>
198[[nodiscard]] constexpr auto tan(E e) { return UnaryFunc<TanTag, E>{e}; }
199
200template<typename E>
201 requires is_expr_node_v<E>
202[[nodiscard]] constexpr auto sinh(E e) { return UnaryFunc<SinhTag, E>{e}; }
203
204template<typename E>
205 requires is_expr_node_v<E>
206[[nodiscard]] constexpr auto cosh(E e) { return UnaryFunc<CoshTag, E>{e}; }
207
208template<typename E>
209 requires is_expr_node_v<E>
210[[nodiscard]] constexpr auto tanh(E e) { return UnaryFunc<TanhTag, E>{e}; }
211
212template<typename E>
213 requires is_expr_node_v<E>
214[[nodiscard]] constexpr auto asin(E e) { return UnaryFunc<AsinTag, E>{e}; }
215
216template<typename E>
217 requires is_expr_node_v<E>
218[[nodiscard]] constexpr auto acos(E e) { return UnaryFunc<AcosTag, E>{e}; }
219
220template<typename E>
221 requires is_expr_node_v<E>
222[[nodiscard]] constexpr auto atan(E e) { return UnaryFunc<AtanTag, E>{e}; }
223
224template<typename E>
225 requires is_expr_node_v<E>
226[[nodiscard]] constexpr auto asinh(E e) { return UnaryFunc<AsinhTag, E>{e}; }
227
228template<typename E>
229 requires is_expr_node_v<E>
230[[nodiscard]] constexpr auto acosh(E e) { return UnaryFunc<AcoshTag, E>{e}; }
231
232template<typename E>
233 requires is_expr_node_v<E>
234[[nodiscard]] constexpr auto atanh(E e) { return UnaryFunc<AtanhTag, E>{e}; }
235
236// Type aliases
237template<typename E> using Exp = UnaryFunc<ExpTag, E>;
238template<typename E> using Log = UnaryFunc<LogTag, E>;
239template<typename E> using Sin = UnaryFunc<SinTag, E>;
240template<typename E> using Cos = UnaryFunc<CosTag, E>;
241template<typename E> using Sqrt = UnaryFunc<SqrtTag, E>;
242template<typename E> using Abs = UnaryFunc<AbsTag, E>;
243template<typename E> using Tan = UnaryFunc<TanTag, E>;
244template<typename E> using Sinh = UnaryFunc<SinhTag, E>;
245template<typename E> using Cosh = UnaryFunc<CoshTag, E>;
246template<typename E> using Tanh = UnaryFunc<TanhTag, E>;
247template<typename E> using Asin = UnaryFunc<AsinTag, E>;
248template<typename E> using Acos = UnaryFunc<AcosTag, E>;
249template<typename E> using Atan = UnaryFunc<AtanTag, E>;
250template<typename E> using Asinh = UnaryFunc<AsinhTag, E>;
251template<typename E> using Acosh = UnaryFunc<AcoshTag, E>;
252template<typename E> using Atanh = UnaryFunc<AtanhTag, E>;
253
254} // namespace limes::expr
constexpr std::string_view tag_name()
Expression layer for composable calculus.
Definition analysis.hpp:7
constexpr auto cos(E e)
constexpr auto acos(E e)
constexpr auto asinh(E e)
constexpr auto log(E e)
constexpr auto sin(E e)
constexpr auto acosh(E e)
constexpr auto atanh(E e)
constexpr auto tanh(E e)
constexpr auto tan(E e)
constexpr auto atan(E e)
constexpr auto sqrt(E e)
constexpr auto exp(E e)
constexpr auto abs(E e)
constexpr auto asin(E e)
constexpr auto cosh(E e)
constexpr auto sinh(E e)
constexpr value_type evaluate(std::span< value_type const > args) const
constexpr UnaryFunc(E c) noexcept
constexpr value_type eval(std::span< value_type const > args) const
typename E::value_type value_type
std::string to_string() const
static constexpr std::size_t arity_v
constexpr auto derivative() const