LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
integration_trigonometric.cpp
浏览该文件的文档.
3
4#include <limits>
5
6namespace LMCAS {
7
8namespace {
9
10// Build sin(c*var) / cos(c*var) where c is a non-zero integer scale.
11std::shared_ptr<SymbolicExpr> make_sin_scaled(const BigInt& scale, const std::string& var) {
12 auto v = SymbolicExpr::variable(var);
13 if (scale == BigInt(1)) { return SymbolicExpr::sin(v); }
14 auto cx = SymbolicExpr::multiply(SymbolicExpr::number(scale), v);
15 return SymbolicExpr::sin(cx);
16}
17
18std::shared_ptr<SymbolicExpr> make_cos_scaled(const BigInt& scale, const std::string& var) {
19 auto v = SymbolicExpr::variable(var);
20 if (scale == BigInt(1)) { return SymbolicExpr::cos(v); }
21 auto cx = SymbolicExpr::multiply(SymbolicExpr::number(scale), v);
22 return SymbolicExpr::cos(cx);
23}
24BigInt trig_binomial(int degree, int selected_degree) {
25 return BigInt::ncr(BigInt(degree), BigInt(selected_degree));
26}
27
28// Match a single-argument FunctionNode whose argument is the integration
29// variable itself. Returns 0 (sin), 1 (cos), 2 (tan), 3 (sec), or -1 on
30// no match for this strategy.
31int trig_match_of_var(const std::shared_ptr<const SymbolicNode>& node, const std::string& var) {
32 auto fn = std::dynamic_pointer_cast<const FunctionNode>(node);
33 if (!fn) { return -1; }
34 if (fn->arguments().size() != 1) { return -1; }
35 auto v = std::dynamic_pointer_cast<const VariableNode>(fn->arguments()[0]);
36 if (!v || v->is_constant() || v->name() != var) { return -1; }
37 using FT = FunctionNode::FuncType;
38 switch (fn->type()) {
39 case FT::Sin: return 0;
40 case FT::Cos: return 1;
41 case FT::Tan: return 2;
42 case FT::Sec: return 3;
43 default: return -1;
44 }
45}
46bool trig_extract_nonneg_int(const std::shared_ptr<const SymbolicNode>& exp_node,
47 int& degree_out, ComputationContext& context) {
48 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
49 (context).limits().max_ast_nodes, "transform");
52 if (!simp) { return false; }
53 const auto number = std::dynamic_pointer_cast<const NumberNode>(LMCAS::detail::node(simp));
54 if (!number) { return false; }
55 const auto degree = integration_bounded_exponent(*number, 0, std::numeric_limits<int>::max());
56 if (!degree) { return false; }
57 degree_out = *degree;
58 return true;
59}
60
61// Match a single factor of the form trig(var) or trig(var)^k where trig is
62// sin/cos/tan/sec and k is a non-negative integer. Returns true on success
63// with the kind (0..3) and the integer power.
64bool trig_extract_factor(const std::shared_ptr<const SymbolicNode>& node,
65 const std::string& var,
66 int& kind_out, int& power_out, ComputationContext& context) {
67 int kind = trig_match_of_var(node, var);
68 if (kind >= 0) {
69 kind_out = kind;
70 power_out = 1;
71 return true;
72 }
73 auto pn = std::dynamic_pointer_cast<const PowerNode>(node);
74 if (!pn) { return false; }
75 int base_kind = trig_match_of_var(pn->base(), var);
76 if (base_kind < 0) { return false; }
77 int p = 0;
78 if (!trig_extract_nonneg_int(pn->exponent(), p, context)) { return false; }
79 kind_out = base_kind;
80 power_out = p;
81 return true;
82}
83
85 const SymbolicExpr& expr, const std::string& var, int& m_out, int& n_out, ComputationContext& context) {
86
87 int m = 0, n = 0;
88 std::vector<std::shared_ptr<const SymbolicNode>> factors;
89 if (auto mul = std::dynamic_pointer_cast<const MultiplyNode>(LMCAS::detail::node(expr))) {
90 factors = mul->operands();
91 } else {
92 factors.push_back(LMCAS::detail::node(expr));
93 }
94
95 for (const auto& f : factors) {
96 int kind = -1, p = 0;
97 if (!trig_extract_factor(f, var, kind, p, context)) { return false; }
98 // Only sin/cos contribute to (m,n); tan/sec disqualify this form.
99 if (kind == 0) {
100 if (p > std::numeric_limits<int>::max() - m) { return false; }
101 m += p;
102 } else if (kind == 1) {
103 if (p > std::numeric_limits<int>::max() - n) { return false; }
104 n += p;
105 }
106 else return false;
107 }
108
109 if (m == 0 && n == 0) { return false; } /** @brief 要求 sin/cos 乘积至少含一个非零次幂。 */
110 m_out = m;
111 n_out = n;
112 return true;
113}
114
116 const SymbolicExpr& expr, const std::string& var, int& n_out, ComputationContext& context) {
117 int kind = -1, p = 0;
118 if (!trig_extract_factor(LMCAS::detail::node(expr), var, kind, p, context)) { return false; }
119 if (kind != 2) { return false; }
120 n_out = p;
121 return true;
122}
123
125 const SymbolicExpr& expr, const std::string& var, int& n_out, ComputationContext& context) {
126 int kind = -1, p = 0;
127 if (!trig_extract_factor(LMCAS::detail::node(expr), var, kind, p, context)) { return false; }
128 if (kind != 3) { return false; }
129 n_out = p;
130 return true;
131}
132
133
134std::shared_ptr<SymbolicExpr> integrate_odd_case(
135 int m, int n, const BigInt& scale, const std::string& var);
136std::shared_ptr<SymbolicExpr> integrate_even_case(
137 int m, int n, const BigInt& scale, const std::string& var);
138
139std::shared_ptr<SymbolicExpr> integrate_sin_m_cos_n(
140 int m, int n, const BigInt& scale, const std::string& var) {
141
142 if (m < 0 || n < 0) { return nullptr; }
143
144 if (m == 0 && n == 0) {
145 // Constant 1 with respect to var: integrand is essentially 1.
146 return SymbolicExpr::variable(var);
147 }
148 if ((m % 2 == 1) || (n % 2 == 1)) {
149 return integrate_odd_case(m, n, scale, var);
150 }
151 return integrate_even_case(m, n, scale, var);
152}
153
154std::shared_ptr<SymbolicExpr> integrate_odd_case(
155 int m, int n, const BigInt& scale, const std::string& var) {
156
157 if (scale == BigInt(0)) { return nullptr; }
158
159 // Pick which factor to peel:
160 // m odd -> peel one sin, set u = cos(scale*x), du = -scale*sin(scale*x)dx
161 // so sin(scale*x)dx = -du/scale.
162 // remaining: sin^(m-1) cos^n = (1-u^2)^k * u^n, k=(m-1)/2.
163 // n odd -> peel one cos, set u = sin(scale*x), du = +scale*cos(scale*x)dx
164 // so cos(scale*x)dx = du/scale.
165 // remaining: sin^m cos^(n-1) = u^m * (1-u^2)^k, k=(n-1)/2.
166 bool peel_sin = (m % 2 == 1);
167 int k = 0;
168 int other_pow = 0;
169 BigInt sign_factor(1); /** @brief 包含换元微分 du 的符号。 */
170 bool u_is_cos = false;
171
172 if (peel_sin) {
173 k = (m - 1) / 2;
174 other_pow = n;
175 sign_factor = -1;
176 u_is_cos = true;
177 } else {
178 k = (n - 1) / 2;
179 other_pow = m;
180 sign_factor = 1;
181 u_is_cos = false;
182 }
183
184 auto u_expr = u_is_cos ? make_cos_scaled(scale, var)
185 : make_sin_scaled(scale, var);
186
187 // Result = sum_{i=0..k} C(k,i)*(-1)^i * u^(2i+other_pow+1) / (scale*(2i+other_pow+1)) * sign_factor
188 std::vector<std::shared_ptr<const SymbolicNode>> add_terms;
189 for (int i = 0; i <= k; ++i) {
190 const BigInt binomial = trig_binomial(k, i);
191 const BigInt alternating_sign((i % 2) == 0 ? 1 : -1);
192 BigInt numerator = sign_factor * alternating_sign * binomial;
193 int u_pow = 2 * i + other_pow + 1; /** @brief 上界为输入总次数加一。 */
194 BigInt denominator = scale * BigInt(u_pow);
195 if (denominator == BigInt(0)) { return nullptr; }
196
197 std::shared_ptr<SymbolicExpr> u_to_pow;
198 if (u_pow == 1) {
199 u_to_pow = u_expr;
200 } else {
201 u_to_pow = SymbolicExpr::power(u_expr, SymbolicExpr::number(u_pow));
202 }
203 // Normalize sign so the rational denominator is positive.
204 if (denominator.is_negative()) { numerator = -numerator; denominator = -denominator; }
205 auto coeff = SymbolicExpr::number(Rational(numerator, denominator));
206 auto term = SymbolicExpr::multiply(coeff, u_to_pow);
207 add_terms.push_back(LMCAS::detail::node(term));
208 }
209
210 if (add_terms.empty()) { return SymbolicExpr::number(0); }
211 if (add_terms.size() == 1) {
212 return LMCAS::detail::make_expression_ptr(add_terms[0]);
213 }
215}
216
217std::shared_ptr<SymbolicExpr> integrate_even_case(
218 int m, int n, const BigInt& scale, const std::string& var) {
219
220 if ((m % 2 != 0) || (n % 2 != 0)) { return nullptr; }
221 int p = m / 2;
222 int q = n / 2;
223 int K = p + q;
224
225 // sin^(2p)(c*x)*cos^(2q)(c*x)
226 // = (1/2^(p+q)) * sum_{i,j} C(p,i)(-1)^i C(q,j) cos^(i+j)(2c*x)
227 // Compute coefficients a[k] = sum_{i+j=k} C(p,i)*C(q,j)*(-1)^i for k=0..K.
228 std::vector<BigInt> a(K + 1, BigInt(0));
229 for (int i = 0; i <= p; ++i) {
230 const BigInt bp = trig_binomial(p, i);
231 const BigInt alternating_sign((i % 2) == 0 ? 1 : -1);
232 for (int j = 0; j <= q; ++j) {
233 const BigInt bq = trig_binomial(q, j);
234 a[i + j] += alternating_sign * bp * bq;
235 }
236 }
237
238 BigInt denominator(1);
239 for (int t = 0; t < K; ++t) denominator *= BigInt(2);
240
241 std::vector<std::shared_ptr<const SymbolicNode>> add_terms;
242 for (int k = 0; k <= K; ++k) {
243 if (a[k] == 0) { continue; }
244 // Recurse: integrate cos^k(2c*x) at the new scale 2c.
245 auto inner = integrate_sin_m_cos_n(0, k, scale * BigInt(2), var);
246 if (!inner) { return nullptr; }
247 auto coeff = SymbolicExpr::number(Rational(a[k], denominator));
248 auto term = SymbolicExpr::multiply(coeff, inner);
249 add_terms.push_back(LMCAS::detail::node(term));
250 }
251
252 if (add_terms.empty()) { return SymbolicExpr::number(0); }
253 if (add_terms.size() == 1) {
254 return LMCAS::detail::make_expression_ptr(add_terms[0]);
255 }
257}
258
259std::shared_ptr<SymbolicExpr> integrate_tan_power(int n, const std::string& var) {
260
261 if (n < 0) { return nullptr; }
262 auto v = SymbolicExpr::variable(var);
263
264 if (n == 0) {
265 // int 1 dx = x
266 return v;
267 }
268 if (n == 1) {
269 // int tan(x) dx = -ln(cos(x))
270 auto cos_x = SymbolicExpr::cos(v);
271 auto ln_cos = SymbolicExpr::ln(cos_x);
272 return SymbolicExpr::multiply(SymbolicExpr::number(-1), ln_cos);
273 }
274
275 // n >= 2: tan^n = tan^(n-2)*(sec^2 - 1)
276 // int tan^n = tan^(n-1)/(n-1) - int tan^(n-2)
277 auto tan_x = SymbolicExpr::tan(v);
278 std::shared_ptr<SymbolicExpr> tan_pow_term;
279 if (n - 1 == 1) {
280 tan_pow_term = tan_x;
281 } else {
282 tan_pow_term = SymbolicExpr::power(tan_x, SymbolicExpr::number(n - 1));
283 }
284 auto first = SymbolicExpr::multiply(sym_rational(1, n - 1), tan_pow_term);
285
286 auto rest = integrate_tan_power(n - 2, var);
287 if (!rest) { return nullptr; }
288 auto neg_rest = SymbolicExpr::multiply(SymbolicExpr::number(-1), rest);
289 return SymbolicExpr::add(first, neg_rest);
290}
291
292std::shared_ptr<SymbolicExpr> integrate_sec_power(int n, const std::string& var) {
293
294 if (n < 2 || (n % 2) != 0) { return nullptr; }
295
296 using FT = FunctionNode::FuncType;
297 auto v = SymbolicExpr::variable(var);
298 auto tan_x = SymbolicExpr::tan(v);
299 auto make_sec_x = [&]() -> std::shared_ptr<SymbolicExpr> {
302 std::vector<std::shared_ptr<const SymbolicNode>>{LMCAS::detail::node(v)}));
303 };
304
305 if (n == 2) {
306 return tan_x;
307 }
308
309 auto sec_x = make_sec_x();
310 std::shared_ptr<SymbolicExpr> sec_pow;
311 if (n - 2 == 1) {
312 sec_pow = sec_x;
313 } else {
314 sec_pow = SymbolicExpr::power(sec_x, SymbolicExpr::number(n - 2));
315 }
316
317 // First term: sec^(n-2)(x) * tan(x) / (n-1)
318 auto inner = SymbolicExpr::multiply(sec_pow, tan_x);
319 auto first = SymbolicExpr::multiply(sym_rational(1, n - 1), inner);
320
321 // Second term: ((n-2)/(n-1)) * int sec^(n-2)
322 auto rest = integrate_sec_power(n - 2, var);
323 if (!rest) { return nullptr; }
324 auto coeff = SymbolicExpr::number(Rational(BigInt(n - 2), BigInt(n - 1)));
325 auto second = SymbolicExpr::multiply(coeff, rest);
326
327 return SymbolicExpr::add(first, second);
328}
329
330}
331
332Result<std::optional<detail::IntegralParts>> Integrator::try_trig_combination(
333 const SymbolicExpr& expr, const std::string& var,
334 ComputationContext& computation, int) {
335 std::shared_ptr<SymbolicExpr> primitive;
336 int m = 0, n = 0;
337 if (extract_sin_cos_powers(expr, var, m, n, computation)) {
338 if (m < 0 || n < 0 || n > 8 || m > 8 - n) {
339 return std::optional<detail::IntegralParts>{};
340 }
341 primitive = integrate_sin_m_cos_n(m, n, BigInt(1), var);
342 } else {
343 int power = 0;
344 if (extract_tan_power(expr, var, power, computation)) {
345 if (power < 2 || power > 8) { return std::optional<detail::IntegralParts>{}; }
346 primitive = integrate_tan_power(power, var);
347 } else if (extract_sec_power(expr, var, power, computation)) {
348 if (power < 2 || power > 8 || power % 2 != 0) {
349 return std::optional<detail::IntegralParts>{};
350 }
351 primitive = integrate_sec_power(power, var);
352 }
353 }
354 if (!primitive) { return std::optional<detail::IntegralParts>{}; }
355 return std::optional<detail::IntegralParts>{{*primitive, zero_}};
356}
357
358
359} // namespace LMCAS
std::shared_ptr< SymbolicExpr > integrate_sec_power(int n, const std::string &var)
int trig_match_of_var(const std::shared_ptr< const SymbolicNode > &node, const std::string &var)
std::shared_ptr< SymbolicExpr > integrate_odd_case(int m, int n, const BigInt &scale, const std::string &var)
bool extract_sin_cos_powers(const SymbolicExpr &expr, const std::string &var, int &m_out, int &n_out, ComputationContext &context)
std::shared_ptr< SymbolicExpr > integrate_even_case(int m, int n, const BigInt &scale, const std::string &var)
bool extract_tan_power(const SymbolicExpr &expr, const std::string &var, int &n_out, ComputationContext &context)
bool trig_extract_nonneg_int(const std::shared_ptr< const SymbolicNode > &exp_node, int &degree_out, ComputationContext &context)
std::shared_ptr< SymbolicExpr > make_sin_scaled(const BigInt &scale, const std::string &var)
std::shared_ptr< SymbolicExpr > integrate_tan_power(int n, const std::string &var)
bool extract_sec_power(const SymbolicExpr &expr, const std::string &var, int &n_out, ComputationContext &context)
std::shared_ptr< SymbolicExpr > integrate_sin_m_cos_n(int m, int n, const BigInt &scale, const std::string &var)
std::shared_ptr< SymbolicExpr > make_cos_scaled(const BigInt &scale, const std::string &var)
bool trig_extract_factor(const std::shared_ptr< const SymbolicNode > &node, const std::string &var, int &kind_out, int &power_out, ComputationContext &context)
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)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
ExprResult e()
__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
ExprResult mul(const ExprPtr &lhs, const ExprPtr &rhs, ComputationContext &context)
std::shared_ptr< SymbolicExpr > sym_rational(const BigInt &numerator, const BigInt &denominator)
__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
std::optional< int > integration_bounded_exponent(const NumberNode &number, int minimum_exponent, int maximum_exponent)
const ExprPtr & denominator
__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