LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
integration_special_functions.cpp
浏览该文件的文档.
3
4namespace LMCAS {
5
6namespace {
7
8
9// Build sqrt(pi).
10inline std::shared_ptr<SymbolicExpr> sf_sqrt_pi() {
11 return SymbolicExpr::sqrt(LMCAS::detail::make_expression_ptr(
13}
14
15// Test whether `node` is a single-argument FunctionNode of the given type
16// whose argument is exactly the integration variable.
17bool sf_is_fn_of_var(const std::shared_ptr<const SymbolicNode>& node,
19 const std::string& var) {
20 auto fn = std::dynamic_pointer_cast<const FunctionNode>(node);
21 if (!fn || fn->type() != t) { return false; }
22 if (fn->arguments().size() != 1) { return false; }
23 auto v = std::dynamic_pointer_cast<const VariableNode>(fn->arguments()[0]);
24 return v && !v->is_constant() && v->name() == var;
25}
26
27// Detect a 1/x factor: a PowerNode whose base is the integration variable and
28// exponent is the integer -1.
29bool sf_is_inv_var(const std::shared_ptr<const SymbolicNode>& node,
30 const std::string& var) {
31 auto pw = std::dynamic_pointer_cast<const PowerNode>(node);
32 if (!pw) { return false; }
33 auto b = std::dynamic_pointer_cast<const VariableNode>(pw->base());
34 if (!b || b->is_constant() || b->name() != var) { return false; }
35 const auto exponent = detail::exact_rational_value(pw->exponent());
36 return exponent && *exponent == Rational(-1);
37}
38
39// Detect a 1/ln(var) factor, i.e. PowerNode(ln(var), -1).
40bool sf_is_inv_ln_var(const std::shared_ptr<const SymbolicNode>& node,
41 const std::string& var) {
42 auto pw = std::dynamic_pointer_cast<const PowerNode>(node);
43 if (!pw) { return false; }
44 if (!sf_is_fn_of_var(pw->base(), FunctionNode::FuncType::Ln, var)) { return false; }
45 const auto exponent = detail::exact_rational_value(pw->exponent());
46 return exponent && *exponent == Rational(-1);
47}
48
49// Split a node into (factors, has_inv_var) where the inv-var factor is removed
50// from `factors` if present. Returns false if there is more than one inv-var
51// factor (which would be 1/x^2 and is not the form we handle here).
52bool sf_split_inv_var(const std::shared_ptr<const SymbolicNode>& node,
53 const std::string& var,
54 std::vector<std::shared_ptr<const SymbolicNode>>& other_factors,
55 bool& has_inv_var) {
56 other_factors.clear();
57 has_inv_var = false;
58 std::vector<std::shared_ptr<const SymbolicNode>> factors;
59 if (auto mul = std::dynamic_pointer_cast<const MultiplyNode>(node)) {
60 factors = mul->operands();
61 } else {
62 factors.push_back(node);
63 }
64 for (const auto& f : factors) {
65 if (sf_is_inv_var(f, var)) {
66 if (has_inv_var) { return false; } /** @brief 多个 1/x 因子形成 1/x^2 类幂次,超出此路径的处理范围。 */
67 has_inv_var = true;
68 } else {
69 other_factors.push_back(f);
70 }
71 }
72 return true;
73}
74
75// Detect exp(-c*var^2) where c is a constant w.r.t. var that is rational and
76// strictly positive. On success, sets `c_out` to that rational coefficient.
77//
78// We accept exp(arg) where the argument is a polynomial in var of degree
79// exactly 2, no constant or linear term, and the leading rational coefficient
80// is strictly negative. (We then return its absolute value as c.)
81Result<bool> sf_match_exp_neg_quad(const std::shared_ptr<const SymbolicNode>& node,
82 const std::string& var,
83 Rational& c_out, ComputationContext& context) {
84 auto fn = std::dynamic_pointer_cast<const FunctionNode>(node);
85 if (!fn || fn->type() != FunctionNode::FuncType::Exp) { return false; }
86 if (fn->arguments().size() != 1) { return false; }
87 auto arg = LMCAS::detail::expression_from_node(fn->arguments()[0]);
88 auto converted = recognize_rational_polynomial(arg, var, context);
89 if (!converted) { return Result<bool>::failure(converted.error()); }
90 if (!converted.value()) { return false; }
91 const auto& poly = *converted.value();
92 if (poly.degree() != 2) { return false; }
93 if (poly.coeffs.size() < 3) { return false; }
94 // Constant and linear terms must be zero.
95 if (!(poly.coeffs[0] == Rational(0))) { return false; }
96 if (!(poly.coeffs[1] == Rational(0))) { return false; }
97 Rational a2 = poly.coeffs[2];
98 // Need a strictly negative coefficient: exp(-c*x^2) with c > 0.
99 if (!(a2 < Rational(0))) { return false; }
100 c_out = Rational(0) - a2; // c = -a2 > 0
101 return true;
102}
103
104} // anonymous namespace
105
106Result<std::optional<detail::IntegralParts>> Integrator::try_special_function(
107 const SymbolicExpr& expr, const std::string& var,
108 ComputationContext& context, int) try {
109
110 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
111 (context).limits().max_ast_nodes, "transform");
112
113 using FT = FunctionNode::FuncType;
114
115 auto v = SymbolicExpr::variable(var);
116
117 if (sf_is_inv_ln_var(LMCAS::detail::node(expr), var)) {
118 auto li = make_unary_function(FT::Li, v);
119 auto simp = detail::make_expression_ptr(detail::normalize_node(detail::node(li), transform_budget));
120 return std::optional<detail::IntegralParts>{{*(simp ? simp : li), zero_}};
121 }
122
123 {
124 Rational c_rat;
125 auto matched = sf_match_exp_neg_quad(LMCAS::detail::node(expr), var, c_rat, context);
126 if (!matched) {
127 return Result<std::optional<detail::IntegralParts>>::failure(matched.error());
128 }
129 if (matched.value()) {
130 // Result: (sqrt(pi) / (2 * sqrt(c))) * erf(sqrt(c) * x)
131 auto sqrt_pi = sf_sqrt_pi();
132 auto sqrt_c = SymbolicExpr::sqrt(SymbolicExpr::number(c_rat));
133 auto scaled_x = SymbolicExpr::multiply(sqrt_c, v);
134 auto erf_node = make_unary_function(FT::Erf, scaled_x);
135
136 auto two = SymbolicExpr::number(2);
137 auto two_sqrt_c = SymbolicExpr::multiply(two, sqrt_c);
138 auto coeff = SymbolicExpr::multiply(
139 sqrt_pi,
140 SymbolicExpr::power(two_sqrt_c, SymbolicExpr::number(-1)));
141 auto result = SymbolicExpr::multiply(coeff, erf_node);
142 auto simp = detail::make_expression_ptr(detail::normalize_node(detail::node(result), transform_budget));
143 return std::optional<detail::IntegralParts>{{*(simp ? simp : result), zero_}};
144 }
145 }
146
147 // These all have shape (something)*1/x, where the "something" is a
148 // FunctionNode of var. Other 1/x patterns (e.g. 1/x alone, x*1/x) are
149 // not our concern.
150 {
151 std::vector<std::shared_ptr<const SymbolicNode>> others;
152 bool has_inv = false;
153 if (sf_split_inv_var(LMCAS::detail::node(expr), var, others, has_inv) && has_inv && others.size() == 1) {
154 const auto& other = others[0];
155 if (sf_is_fn_of_var(other, FT::Exp, var)) {
156 auto ei = make_unary_function(FT::Ei, v);
157 auto simp = detail::make_expression_ptr(detail::normalize_node(detail::node(ei), transform_budget));
158 return std::optional<detail::IntegralParts>{{*(simp ? simp : ei), zero_}};
159 }
160 if (sf_is_fn_of_var(other, FT::Sin, var)) {
161 auto si = make_unary_function(FT::Si, v);
162 auto simp = detail::make_expression_ptr(detail::normalize_node(detail::node(si), transform_budget));
163 return std::optional<detail::IntegralParts>{{*(simp ? simp : si), zero_}};
164 }
165 if (sf_is_fn_of_var(other, FT::Cos, var)) {
166 auto ci = make_unary_function(FT::Ci, v);
167 auto simp = detail::make_expression_ptr(detail::normalize_node(detail::node(ci), transform_budget));
168 return std::optional<detail::IntegralParts>{{*(simp ? simp : ci), zero_}};
169 }
170 }
171 }
172
173 return std::optional<detail::IntegralParts>{};
174} catch (const CasError& error) {
175 return Result<std::optional<detail::IntegralParts>>::failure(error);
176}
177
178
179} // namespace LMCAS
static Result failure(CasErrc code, std::string message, std::string operation={})
bool sf_is_fn_of_var(const std::shared_ptr< const SymbolicNode > &node, FunctionNode::FuncType t, const std::string &var)
Result< bool > sf_match_exp_neg_quad(const std::shared_ptr< const SymbolicNode > &node, const std::string &var, Rational &c_out, ComputationContext &context)
bool sf_is_inv_ln_var(const std::shared_ptr< const SymbolicNode > &node, const std::string &var)
bool sf_is_inv_var(const std::shared_ptr< const SymbolicNode > &node, const std::string &var)
bool sf_split_inv_var(const std::shared_ptr< const SymbolicNode > &node, const std::string &var, std::vector< std::shared_ptr< const SymbolicNode > > &other_factors, bool &has_inv_var)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
inline ::LMCAS::SymbolicExpr expression_from_node(SymbolicNodePtr root)
std::shared_ptr< const Node > make_node(Args &&... args)
std::optional< Rational > exact_rational_value(const NumberNode &number)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
__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 const std::string & v
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
@ Rational
Rational numbers
ExprResult mul(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
const MultiPoly & b
const ExprPtr & exponent
Result< OptionalRationalPolynomial > recognize_rational_polynomial(const SymbolicExpr &expression, const std::string &variable, ComputationContext &context)
std::shared_ptr< SymbolicExpr > make_unary_function(FunctionNode::FuncType type, const std::shared_ptr< SymbolicExpr > &operand)
__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 & t