LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
expr_complex_evaluation.cpp
浏览该文件的文档.
1#include "expr.hpp"
2
3#include <cmath>
4#include <exception>
5#include <limits>
6#include <utility>
7
11#include "lmmc/complex.h"
12
13namespace LMCAS {
14namespace {
15
16constexpr const char* kEvalfOperation = "LMCAS.evalf";
17
19 const char* operation) {
20 return Result<ApproxComplex>::failure(code, std::move(message), operation);
21}
22
26
27ApproxReal approx_part(double value, double error = 0.0) {
28 ApproxReal part;
29 part.value = value;
30 part.absolute_error = error;
32 return part;
33}
34
38
40 const char* operation) {
41 if (!std::isfinite(real) || !std::isfinite(imag)) {
43 "complex evaluation produced a non-finite component",
44 operation);
45 }
46 const double unknown = std::numeric_limits<double>::infinity();
48 ApproxComplex{approx_part(real, unknown), approx_part(imag, unknown)});
49}
50
52 if (!real) {
54 }
55 if (!real.value().is_finite() || !std::isfinite(real.value().value)) {
57 "complex evaluation requires finite real components",
59 }
62}
63
65 const ApproxComplex& rhs) {
66 return checked_complex(lhs.real.value + rhs.real.value,
67 lhs.imag.value + rhs.imag.value,
69}
70
72 const ApproxComplex& rhs) {
73 const double a = lhs.real.value;
74 const double b = lhs.imag.value;
75 const double c = rhs.real.value;
76 const double d = rhs.imag.value;
77 const lmmc_complex_t left{a, b};
78 const lmmc_complex_t right{c, d};
79 lmmc_complex_t product;
80 if (lmmc_complex_mul(&left, &right, &product) != LMMC_STATUS_OK) {
82 "complex multiplication requires finite components and result",
84 }
85 return checked_complex(product.real, product.imag, kEvalComplexOperation);
86}
87
89 const ApproxComplex& rhs) {
90 const double a = lhs.real.value;
91 const double b = lhs.imag.value;
92 const double c = rhs.real.value;
93 const double d = rhs.imag.value;
94 if (c == 0.0 && d == 0.0) {
96 "complex division by zero",
98 }
99 const lmmc_complex_t left{a, b};
100 const lmmc_complex_t right{c, d};
101 lmmc_complex_t quotient;
102 if (lmmc_complex_div(&left, &right, &quotient) != LMMC_STATUS_OK) {
104 "complex division requires finite components and result",
106 }
107 return checked_complex(quotient.real, quotient.imag, kEvalComplexOperation);
108}
109
110Result<ApproxComplex> evaluate_complex_node(
111 const std::shared_ptr<const SymbolicNode>& node,
114
116 const ComplexNode& node, const NumericBindings& bindings,
118 auto real = evaluate_numeric(
120 if (!real) {
122 }
123 auto imag = evaluate_numeric(
125 if (!imag) {
126 return eval_complex_failure(imag.error());
127 }
128 if (!real.value().is_finite() || !imag.value().is_finite() ||
129 !std::isfinite(real.value().value) ||
130 !std::isfinite(imag.value().value)) {
132 "complex components must be finite",
134 }
136 ApproxComplex{real.value(), imag.value()});
137}
138
139template <typename Combine>
141 const std::vector<std::shared_ptr<const SymbolicNode>>& operands,
142 ApproxComplex identity, Combine combine, const NumericBindings& bindings,
144 auto result = Result<ApproxComplex>::success(identity);
145 for (const auto& operand : operands) {
147 if (!value) {
148 return value;
149 }
150 result = combine(result.value(), value.value());
151 if (!result) {
152 return result;
153 }
154 }
155 return result;
156}
157
159 const std::shared_ptr<const SymbolicNode>& node,
161 auto expression = simplify(detail::make_expression_ptr(node), context);
162 if (!expression) {
163 return complex_failure(expression.error().code,
164 std::move(expression.error().message),
166 }
167 const auto* number = dynamic_cast<const NumberNode*>(
168 detail::node(expression.value()).get());
169 const auto* rational = number ? std::get_if<Rational>(&number->value()) : nullptr;
170 auto exponent = evaluate_complex_node(detail::node(expression.value()),
172 if (!exponent) return exponent;
173 if (rational && rational->get_denominator() != BigInt(1) &&
174 std::trunc(exponent.value().real.value) == exponent.value().real.value) {
176 "exact fractional exponent rounds to an integer",
178 }
179 return exponent;
180}
181
183 unsigned magnitude = static_cast<unsigned>(std::abs(integer));
184 auto factor = base;
185 if (integer < 0) {
186 factor = divide_complex(approx_complex(1.0, 0.0), base.value());
187 if (!factor) {
188 return factor;
189 }
190 }
191 auto result = Result<ApproxComplex>::success(approx_complex(1.0, 0.0));
192 if (magnitude == 0U) {
193 return checked_complex(1.0, 0.0, kEvalComplexOperation);
194 }
195 while (magnitude != 0U) {
196 if ((magnitude & 1U) != 0U) {
197 result = multiply_complex(result.value(), factor.value());
198 if (!result) {
199 return result;
200 }
201 }
202 magnitude >>= 1U;
203 if (magnitude != 0U) {
204 factor = multiply_complex(factor.value(), factor.value());
205 if (!factor) {
206 return factor;
207 }
208 }
209 }
210 return result;
211}
212
214 const PowerNode& power, const NumericBindings& bindings,
216 auto base = evaluate_complex_node(power.base(), bindings, context);
217 if (!base) {
218 return base;
219 }
221 if (!exponent) {
222 return Result<ApproxComplex>::failure(std::move(exponent.error()));
223 }
224 if (exponent.value().imag.value != 0.0 ||
225 std::trunc(exponent.value().real.value) != exponent.value().real.value) {
226 const lmmc_complex_t value{base.value().real.value, base.value().imag.value};
227 const lmmc_complex_t power{exponent.value().real.value,
228 exponent.value().imag.value};
229 lmmc_complex_t result;
230 const auto status = lmmc_complex_pow(&value, &power, &result);
231 if (status == LMMC_STATUS_OUT_OF_RANGE) {
232 return complex_failure(CasErrc::DomainError, "complex power of zero",
234 }
235 if (status != LMMC_STATUS_OK) {
237 "complex power produced a non-finite result",
239 }
240 return checked_complex(result.real, result.imag, kEvalComplexOperation);
241 }
242 if (std::abs(exponent.value().real.value) > 64.0) {
244 "complex evaluation only supports integer powers with |n| <= 64",
246 }
247 return integer_complex_power(std::move(base), static_cast<int>(exponent.value().real.value));
248}
249
251 const std::shared_ptr<const SymbolicNode>& node,
254 auto entered = context.enter_recursion(kEvalComplexOperation);
255 if (!entered) {
256 return Result<ApproxComplex>::failure(entered.error());
257 }
258 struct RecursionExit {
260 ~RecursionExit() { context.leave_recursion(); }
261 } recursion_exit{context};
262 if (!node) {
264 "expression contains a null node",
266 }
267
268 if (auto complex_node = std::dynamic_pointer_cast<const ComplexNode>(node)) {
269 return evaluate_complex_components(*complex_node, bindings, context);
270 }
271 if (auto add = std::dynamic_pointer_cast<const AddNode>(node)) {
273 add->operands(), approx_complex(0.0, 0.0), add_complex, bindings, context);
274 }
275 if (auto multiply = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
277 multiply->operands(), approx_complex(1.0, 0.0), multiply_complex,
279 }
280 if (auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
281 return evaluate_complex_power(*power, bindings, context);
282 }
283
284 return real_to_complex(
286 bindings, context));
287}
288
289}
290
291Result<ApproxReal> evalf(const SymbolicExpr& expression,
294 auto evaluated = evaluate_numeric(expression, bindings, context);
295 if (!evaluated) { return evaluated; }
296 if (!evaluated.value().is_finite() ||
297 !std::isfinite(evaluated.value().value)) {
300 "LMCAS evalf produced a non-finite result",
301 kEvalfOperation);
302 }
303 return evaluated;
304}
305
306Result<ApproxReal> evalf(const SymbolicExpr& expression,
307 const NumericBindings& bindings) {
309 return evalf(expression, bindings, context);
310}
311
312Result<ApproxComplex> eval_complex(const SymbolicExpr& expression,
315 try {
316 if (!LMCAS::detail::node(expression)) {
317 return complex_failure(CasErrc::InvalidArgument,
318 "cannot evaluate an empty expression as complex",
320 }
321 return evaluate_complex_node(LMCAS::detail::node(expression),
323 } catch (const std::bad_alloc&) {
324 return complex_failure(CasErrc::ResourceLimit,
325 "complex evaluation allocation failed",
327 } catch (const std::exception& error) {
328 return complex_failure(CasErrc::InternalInvariant, error.what(),
330 }
331}
332
333Result<ApproxComplex> eval_complex(const SymbolicExpr& expression,
334 const NumericBindings& bindings) {
336 return eval_complex(expression, bindings, context);
337}
338
339}
复数节点,表示 real + imag * i。
幂运算节点,表示 base^exponent。
const std::shared_ptr< const SymbolicNode > & exponent() const noexcept
const std::shared_ptr< const SymbolicNode > & base() const noexcept
CasError & error()
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
Result< ApproxComplex > real_to_complex(const Result< ApproxReal > &real)
Result< ApproxComplex > evaluate_complex_power(const PowerNode &power, const NumericBindings &bindings, ComputationContext &context)
Result< ApproxComplex > complex_failure(CasErrc code, std::string message, const char *operation)
Result< ApproxComplex > evaluate_complex_node(const std::shared_ptr< const SymbolicNode > &node, const NumericBindings &bindings, ComputationContext &context)
Result< ApproxComplex > divide_complex(const ApproxComplex &lhs, const ApproxComplex &rhs)
Result< ApproxComplex > evaluate_complex_components(const ComplexNode &node, const NumericBindings &bindings, ComputationContext &context)
Result< ApproxComplex > multiply_complex(const ApproxComplex &lhs, const ApproxComplex &rhs)
Result< ApproxComplex > integer_complex_power(Result< ApproxComplex > base, int integer)
Result< ApproxComplex > eval_complex_failure(const CasError &error)
Result< ApproxComplex > add_complex(const ApproxComplex &lhs, const ApproxComplex &rhs)
Result< ApproxComplex > evaluate_complex_operands(const std::vector< std::shared_ptr< const SymbolicNode > > &operands, ApproxComplex identity, Combine combine, const NumericBindings &bindings, ComputationContext &context)
Result< ApproxComplex > evaluate_complex_exponent(const std::shared_ptr< const SymbolicNode > &node, const NumericBindings &bindings, ComputationContext &context)
Result< ApproxComplex > checked_complex(double real, double imag, const char *operation)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< ApproxReal > evalf(const SymbolicExpr &expression, const NumericBindings &bindings, ComputationContext &context)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
constexpr const char * kEvalComplexOperation
ExprResult integer(Integer)=delete
ExprResult real(const ExprPtr &expression, ComputationContext &context)
const VectorField const VectorField & c
ExprResult add(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
const MultiPoly & b
std::string message
Result< ApproxComplex > eval_complex(const SymbolicExpr &expression, const NumericBindings &bindings, ComputationContext &context)
ExprResult simplify(const ExprPtr &expression, ComputationContext &context)
const ExprPtr & exponent
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::shared_ptr< SymbolicExpr > & a
const std::vector< Binding > & bindings
ExprPtr imag
const ExprPtr & right
const std::string const ExprPtr & value
const ExprPtr & rhs
Result< ApproxReal > evaluate_numeric(const SymbolicExpr &expression, const NumericBindings &bindings={})
std::unordered_map< std::string, double > NumericBindings
ExprResult rational(const Rational &value)
内部 AST 定义。