LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
series_engine.cpp
浏览该文件的文档.
1#include <stdexcept>
3/**
4 * @file series_engine.cpp
5 */
6
7#include "series_engine.hpp"
8#include "symbolic.hpp"
10#include "integration.hpp"
14#include <cstdint>
15#include <memory>
16#include <string>
17#include <cstdio>
18#include <typeinfo>
19#include <algorithm>
20#include <limits>
21#include <optional>
22
23
24namespace LMCAS {
25
26namespace {
27
28
30 int order,
32 const std::string& operation)
33{
34 auto step = context.consume_steps(1, operation);
35 if (!step) {
36 return step;
37 }
38 if (order <= 0) {
41 "power series truncation order must be positive",
42 operation);
43 }
44 return Result<void>::success();
45}
46
47
48
49
50} // namespace
51namespace detail::series_support {
52
55 const std::string& operation)
56{
57 auto step = context.consume_steps(1, operation);
58 if (!step) {
59 return step;
60 }
61 if (var.empty()) {
63 "series variable name cannot be empty",
64 operation);
65 }
66 return Result<void>::success();
67}
68
69std::optional<int> integer_value_from_node(const std::shared_ptr<const SymbolicNode>& node)
70{
71 BigInt integer;
72 if (!try_get_integer_value(std::dynamic_pointer_cast<const NumberNode>(node), integer)) {
73 return std::nullopt;
74 }
75 auto value = integer.try_to_int64();
76 if (!value || *value < std::numeric_limits<int>::min() ||
77 *value > std::numeric_limits<int>::max()) {
78 return std::nullopt;
79 }
80 return static_cast<int>(*value);
81}
82
83static std::optional<int> laurent_power_of_power(
84 const PowerNode& power, const std::string& var) {
86 if (!exponent) {
87 return std::nullopt;
88 }
89 auto base = std::dynamic_pointer_cast<const VariableNode>(power.base());
90 if (base && !base->is_constant() && base->name() == var) {
91 return *exponent;
92 }
93 auto nested = supported_laurent_integer_power(power.base(), var);
94 if (!nested) {
95 return std::nullopt;
96 }
97 const BigInt product = BigInt(*nested) * BigInt(*exponent);
98 if (product < BigInt(std::numeric_limits<int>::min()) ||
99 product > BigInt(std::numeric_limits<int>::max())) {
100 return std::nullopt;
101 }
102 return static_cast<int>(*product.try_to_int64());
103}
104
106 const std::shared_ptr<const SymbolicNode>& node,
107 const std::string& var)
108{
109 if (!node) {
110 return std::nullopt;
111 }
112 if (std::dynamic_pointer_cast<const NumberNode>(node)) {
113 return 0;
114 }
115 if (auto variable = std::dynamic_pointer_cast<const VariableNode>(node)) {
116 return !variable->is_constant() && variable->name() == var
117 ? std::optional<int>(1) : std::nullopt;
118 }
119 if (auto power = std::dynamic_pointer_cast<const PowerNode>(node)) {
120 return laurent_power_of_power(*power, var);
121 }
122 if (auto multiply = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
123 BigInt total_power(0);
124 for (const auto& operand : multiply->operands()) {
125 if (std::dynamic_pointer_cast<const NumberNode>(operand)) {
126 continue;
127 }
128 auto power = supported_laurent_integer_power(operand, var);
129 if (!power) {
130 return std::nullopt;
131 }
132 total_power += BigInt(*power);
133 }
134 if (total_power < BigInt(std::numeric_limits<int>::min()) ||
135 total_power > BigInt(std::numeric_limits<int>::max())) {
136 return std::nullopt;
137 }
138 return static_cast<int>(*total_power.try_to_int64());
139 }
140 return std::nullopt;
141
142
143}
144
146 const std::vector<std::shared_ptr<SymbolicExpr>>& coeffs,
147 const std::string& operation,
148 const std::string& name)
149{
150 for (size_t i = 0; i < coeffs.size(); ++i) {
151 if (!coeffs[i] || !LMCAS::detail::node(coeffs[i])) {
154 name + " contains a null coefficient at index " + std::to_string(i),
155 operation);
156 }
157 }
158 return Result<void>::success();
159}
160} // namespace detail::series_support
161
163
164std::vector<std::shared_ptr<SymbolicExpr>> power_series_add(
165 const std::vector<std::shared_ptr<SymbolicExpr>>& a,
166 const std::vector<std::shared_ptr<SymbolicExpr>>& b) {
168 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
169 (context).limits().max_ast_nodes, "transform");
170 size_t len = std::max(a.size(), b.size());
171 std::vector<std::shared_ptr<SymbolicExpr>> result(len);
172 for (size_t i = 0; i < len; ++i) {
173 auto ai = (i < a.size() && a[i]) ? a[i] : SymbolicExpr::number(0);
174 auto bi = (i < b.size() && b[i]) ? b[i] : SymbolicExpr::number(0);
175 result[i] = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::add(ai, bi)), transform_budget));
176 }
177 return result;
178}
179
181 const std::vector<std::shared_ptr<SymbolicExpr>>& a,
182 const std::vector<std::shared_ptr<SymbolicExpr>>& b, int order,
184 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
185 (context).limits().max_ast_nodes, "transform");
186 const std::string operation = "power_series_multiply";
187 auto order_check = validate_power_series_order(order, context, operation);
188 if (!order_check) {
189 return PowerSeriesResult::failure(order_check.error());
190 }
191 auto a_check = validate_power_series_coefficients(a, operation, "left series");
192 if (!a_check) {
193 return PowerSeriesResult::failure(a_check.error());
194 }
195 auto b_check = validate_power_series_coefficients(b, operation, "right series");
196 if (!b_check) {
197 return PowerSeriesResult::failure(b_check.error());
198 }
199
200 size_t n = static_cast<size_t>(order);
201 std::vector<std::shared_ptr<SymbolicExpr>> result(n, SymbolicExpr::number(0));
202 for (size_t k = 0; k < n; ++k) {
203 auto step = context.consume_steps(1, operation);
204 if (!step) {
205 return PowerSeriesResult::failure(step.error());
206 }
207 std::vector<std::shared_ptr<const SymbolicNode>> terms;
208 for (size_t j = 0; j <= k; ++j) {
209 if (j >= a.size() || (k-j) >= b.size()) {
210 continue;
211 }
212 auto aj = a[j] ? a[j] : SymbolicExpr::number(0);
213 auto bkj = b[k-j] ? b[k-j] : SymbolicExpr::number(0);
214 terms.push_back(LMCAS::detail::node(SymbolicExpr::multiply(aj, bkj)));
215 }
216 if (terms.empty()) {
217 result[k] = SymbolicExpr::number(0);
218 } else if (terms.size() == 1) {
219 result[k] = detail::make_expression_ptr(detail::normalize_node(terms[0], transform_budget));
220 } else {
222 }
223 }
224 return PowerSeriesResult::success(std::move(result));
225} catch (const CasError& error) {
226 return PowerSeriesResult::failure(error);
227} catch (const std::bad_alloc&) {
228 return PowerSeriesResult::failure(CasErrc::ResourceLimit, "allocation failed", "power_series_multiply");
229} catch (const std::length_error& error) {
230 return PowerSeriesResult::failure(CasErrc::ResourceLimit, error.what(), "power_series_multiply");
231} catch (const std::exception& error) {
232 return PowerSeriesResult::failure(CasErrc::InternalInvariant, error.what(), "power_series_multiply");
233}
234
236 const std::vector<std::shared_ptr<SymbolicExpr>>& a,
237 const std::vector<std::shared_ptr<SymbolicExpr>>& b, int order) {
240}
241
242
244 const std::vector<std::shared_ptr<SymbolicExpr>>& g,
245 std::vector<std::vector<std::shared_ptr<SymbolicExpr>>>& gp,
246 int order, ComputationContext& context, const std::string& operation) {
247 const size_t n = gp.size();
248 gp[0].resize(n, SymbolicExpr::number(0));
249 gp[0][0] = SymbolicExpr::number(1);
250 if (n > 1) {
251 gp[1].resize(n, SymbolicExpr::number(0));
252 for (size_t i = 0; i < std::min(g.size(), n); ++i) {
253 gp[1][i] = g[i] ? g[i] : SymbolicExpr::number(0);
254 }
255 }
256 for (size_t k = 2; k < n; ++k) {
257 auto step = context.consume_steps(1, operation);
258 if (!step) {
259 return step;
260 }
261 auto multiplied = power_series_multiply_checked(gp[k-1], gp[1], order, context);
262 if (!multiplied) {
263 return Result<void>::failure(multiplied.error());
264 }
265 gp[k] = std::move(multiplied.value());
266 }
267 return Result<void>::success();
268}
269
271 const std::vector<std::shared_ptr<SymbolicExpr>>& f,
272 const std::vector<std::shared_ptr<SymbolicExpr>>& g, int order,
274 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
275 (context).limits().max_ast_nodes, "transform");
276 const std::string operation = "power_series_compose";
277 auto order_check = validate_power_series_order(order, context, operation);
278 if (!order_check) {
279 return PowerSeriesResult::failure(order_check.error());
280 }
281 auto f_check = validate_power_series_coefficients(f, operation, "outer series");
282 if (!f_check) {
283 return PowerSeriesResult::failure(f_check.error());
284 }
285 auto g_check = validate_power_series_coefficients(g, operation, "inner series");
286 if (!g_check) {
287 return PowerSeriesResult::failure(g_check.error());
288 }
289
290 size_t n = static_cast<size_t>(order);
291 if (!g.empty() && !g[0]->is_zero()) {
294 "power series composition around zero requires g(0) = 0",
295 operation);
296 }
297 std::vector<std::shared_ptr<SymbolicExpr>> result(n, SymbolicExpr::number(0));
298 std::vector<std::vector<std::shared_ptr<SymbolicExpr>>> gp(n);
299 auto powers = composition_inner_powers(g, gp, order, context, operation);
300 if (!powers) {
301 return PowerSeriesResult::failure(powers.error());
302 }
303 for (size_t k = 0; k < std::min(f.size(), n); ++k) {
304 auto step = context.consume_steps(1, operation);
305 if (!step) {
306 return PowerSeriesResult::failure(step.error());
307 }
308 auto fk = f[k] ? f[k] : SymbolicExpr::number(0);
309 if (fk->is_zero()) {
310 continue;
311 }
312 for (size_t i = 0; i < n && i < gp[k].size(); ++i)
313 result[i] = detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::add(result[i], SymbolicExpr::multiply(fk, gp[k][i]))), transform_budget));
314 }
315 return PowerSeriesResult::success(std::move(result));
316} catch (const CasError& error) {
317 return PowerSeriesResult::failure(error);
318} catch (const std::bad_alloc&) {
319 return PowerSeriesResult::failure(CasErrc::ResourceLimit, "allocation failed", "power_series_compose");
320} catch (const std::length_error& error) {
321 return PowerSeriesResult::failure(CasErrc::ResourceLimit, error.what(), "power_series_compose");
322} catch (const std::exception& error) {
323 return PowerSeriesResult::failure(CasErrc::InternalInvariant, error.what(), "power_series_compose");
324}
325
327 const std::vector<std::shared_ptr<SymbolicExpr>>& f,
328 const std::vector<std::shared_ptr<SymbolicExpr>>& g, int order) {
331}
332
333
334
335std::shared_ptr<SymbolicExpr> symbolic_sum(
336 const std::shared_ptr<SymbolicExpr>& body, const std::string& index,
337 const std::shared_ptr<SymbolicExpr>& lower, const std::shared_ptr<SymbolicExpr>& upper) {
339 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
340 (context).limits().max_ast_nodes, "transform");
341 if (!body || !lower || !upper) {
342 return nullptr;
343 }
344 BigInt lv, uv;
346 std::dynamic_pointer_cast<const NumberNode>(LMCAS::detail::node(lower)), lv) &&
348 std::dynamic_pointer_cast<const NumberNode>(LMCAS::detail::node(upper)), uv)) {
349 if (uv < lv) {
350 return SymbolicExpr::number(0);
351 }
352 const BigInt span = uv - lv;
353 if (span < BigInt(100)) {
354 const auto iterations = *span.try_to_uint64() + 1;
355 std::vector<std::shared_ptr<const SymbolicNode>> terms;
356 BigInt k = lv;
357 const BigInt increment(1);
358 for (std::uint64_t iteration = 0; iteration < iterations; ++iteration, k += increment) {
359 auto val = SymbolicExpr::number(k);
360 auto term = detail::make_expression_ptr(detail::substitute_node(detail::node(body), index, detail::node(val), transform_budget));
361 if (term) { term = detail::make_expression_ptr(detail::normalize_node(detail::node(term), transform_budget)); terms.push_back(LMCAS::detail::node(term)); }
362 }
363 if (terms.empty()) {
364 return SymbolicExpr::number(0);
365 }
366 if (terms.size() == 1) {
367 return detail::make_expression_ptr(detail::normalize_node(terms[0], transform_budget));
368 }
370 }
371 }
372 auto k_var = SymbolicExpr::variable(index);
373 if (body->to_string() == k_var->to_string()) {
374 auto u = upper; auto l = lower;
375 auto u1 = SymbolicExpr::add(u, SymbolicExpr::number(1));
376 auto su = SymbolicExpr::divide(SymbolicExpr::multiply(u, u1), SymbolicExpr::number(2));
377 auto lm1 = SymbolicExpr::add(l, SymbolicExpr::number(-1));
378 auto sl = SymbolicExpr::divide(SymbolicExpr::multiply(lm1, l), SymbolicExpr::number(2));
379 return detail::make_expression_ptr(detail::normalize_node(detail::node(SymbolicExpr::add(su, SymbolicExpr::multiply(SymbolicExpr::number(-1), sl))), transform_budget));
380 }
382}
383
384std::shared_ptr<SymbolicExpr> symbolic_product(
385 const std::shared_ptr<SymbolicExpr>& body, const std::string& index,
386 const std::shared_ptr<SymbolicExpr>& lower, const std::shared_ptr<SymbolicExpr>& upper) {
388 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
389 (context).limits().max_ast_nodes, "transform");
390 if (!body || !lower || !upper) {
391 return nullptr;
392 }
393 BigInt lv, uv;
395 std::dynamic_pointer_cast<const NumberNode>(LMCAS::detail::node(lower)), lv) &&
397 std::dynamic_pointer_cast<const NumberNode>(LMCAS::detail::node(upper)), uv)) {
398 if (uv < lv) {
399 return SymbolicExpr::number(1);
400 }
401 const BigInt span = uv - lv;
402 if (span < BigInt(50)) {
403 const auto iterations = *span.try_to_uint64() + 1;
404 auto result = SymbolicExpr::number(1);
405 BigInt k = lv;
406 const BigInt increment(1);
407 for (std::uint64_t iteration = 0; iteration < iterations; ++iteration, k += increment) {
408 auto val = SymbolicExpr::number(k);
409 auto term = detail::make_expression_ptr(detail::substitute_node(detail::node(body), index, detail::node(val), transform_budget));
410 if (term) { term = detail::make_expression_ptr(detail::normalize_node(detail::node(term), transform_budget)); result = SymbolicExpr::multiply(result, term); }
411 }
412 return detail::make_expression_ptr(detail::normalize_node(detail::node(result), transform_budget));
413 }
414 }
416}
417
418
419
420} // namespace LMCAS
幂运算节点,表示 base^exponent。
const std::shared_ptr< const SymbolicNode > & exponent() const noexcept
const std::shared_ptr< const SymbolicNode > & base() const noexcept
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(T value)
Symbolic integration APIs.
Result< void > validate_power_series_order(int order, ComputationContext &context, const std::string &operation)
static std::optional< int > laurent_power_of_power(const PowerNode &power, const std::string &var)
std::optional< int > integer_value_from_node(const std::shared_ptr< const SymbolicNode > &node)
Result< void > validate_power_series_coefficients(const std::vector< std::shared_ptr< SymbolicExpr > > &coefficients, const std::string &operation, const std::string &name)
std::optional< int > supported_laurent_integer_power(const std::shared_ptr< const SymbolicNode > &node, const std::string &variable)
Result< void > validate_series_variable(const std::string &variable, ComputationContext &context, const std::string &operation)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
const std::string & variable
std::shared_ptr< const Node > make_node(Args &&... args)
ComputationContext const std::string & operation
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::size_t std::size_t ComputationContext & context
SymbolicNodePtr substitute_node(const SymbolicNodePtr &node, const std::string &variable, const SymbolicNodePtr &replacement, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
std::shared_ptr< SymbolicExpr > symbolic_product(const std::shared_ptr< SymbolicExpr > &body, const std::string &index, const std::shared_ptr< SymbolicExpr > &lower, const std::shared_ptr< SymbolicExpr > &upper)
__attribute__((visibility("default"))) ExprResult function(const std __attribute__((visibility("default"))) ExprResult finite_set(std ExprPtr upper
static Result< void > composition_inner_powers(const std::vector< std::shared_ptr< SymbolicExpr > > &g, std::vector< std::vector< std::shared_ptr< SymbolicExpr > > > &gp, int order, ComputationContext &context, const std::string &operation)
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
PowerSeriesResult power_series_multiply_checked(const std::vector< std::shared_ptr< SymbolicExpr > > &a, const std::vector< std::shared_ptr< SymbolicExpr > > &b, int order, ComputationContext &context)
ExprResult integer(Integer)=delete
bool try_get_integer_value(const std::shared_ptr< const NumberNode > &node, BigInt &value)
std::shared_ptr< SymbolicExpr > symbolic_sum(const std::shared_ptr< SymbolicExpr > &body, const std::string &index, const std::shared_ptr< SymbolicExpr > &lower, const std::shared_ptr< SymbolicExpr > &upper)
const ExprPtr & lower
const std::vector< FGLMPoly > const MonomialOrder & order
Result< std::vector< std::shared_ptr< SymbolicExpr > > > PowerSeriesResult
const MultiPoly & b
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
Result< void > validate_power_series_coefficients(const std::vector< std::shared_ptr< SymbolicExpr > > &coefficients, const std::string &operation, const std::string &name)
PowerSeriesResult power_series_compose_checked(const std::vector< std::shared_ptr< SymbolicExpr > > &f, const std::vector< std::shared_ptr< SymbolicExpr > > &g, int order, ComputationContext &context)
const std::string const ExprPtr & value
std::vector< std::shared_ptr< SymbolicExpr > > power_series_add(const std::vector< std::shared_ptr< SymbolicExpr > > &a, const std::vector< std::shared_ptr< SymbolicExpr > > &b)
__attribute__((visibility("default"))) Result< EqvProfile > eqv_profile_from_name(const std const std::string & name
__attribute__((visibility("default"))) VectorCalculusExprResult surface_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult surface_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string & u
级数引擎模块:收敛性判定,幂级数运算,傅里叶级数,洛朗级数,渐近展开,符号求和与乘积.
符号表达式主类 SymbolicExpr 及其工厂方法,运算接口.
内部 AST 定义。