LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
ode_characteristic_roots.cpp
浏览该文件的文档.
2
3#include "lmmc/numeric.h"
4
5#include <algorithm>
6#include <cmath>
7#include <limits>
8#include <utility>
9
10namespace LMCAS::ode_root_detail {
11
12namespace {
13constexpr double root_verification_tolerance = 1e-8;
14
16 const RealPolynomial& norm_coeffs, const std::string& operation) {
17 std::vector<CharRoot> roots;
18 const double b = norm_coeffs[1];
19 const double c = norm_coeffs[2];
20 const double scale = std::max(std::abs(b), std::sqrt(std::abs(c)));
21 const double scaled_b = scale == 0.0 ? 0.0 : b / scale;
22 const double scaled_c =
23 scale == 0.0 ? 0.0 : (c / scale) / scale;
24 const double discriminant =
25 std::fma(scaled_b, scaled_b, -4.0 * scaled_c);
26 if (discriminant == 0.0) {
27 roots.push_back({-b / 2.0, 0.0, 2, false});
28 } else if (discriminant > 0.0) {
29 const double sqrt_discriminant =
30 scale * std::sqrt(discriminant);
31 const double q =
32 -0.5 * (b + std::copysign(sqrt_discriminant, b));
33 roots.push_back({q, 0.0, 1, false});
34 roots.push_back({c / q, 0.0, 1, false});
35 } else {
36 roots.push_back(
37 {-b / 2.0, scale * std::sqrt(-discriminant) / 2.0,
38 1, true});
39 }
40 for (const auto& root : roots) {
42 norm_coeffs, {root.real_part, root.imag_part}) >
44 return Result<std::vector<CharRoot>>::failure(
46 "quadratic characteristic root failed verification",
47 operation);
48 }
49 }
50 return Result<std::vector<CharRoot>>::success(std::move(roots));
51
52}
53
55 const RealPolynomial& norm_coeffs, const std::string& operation) {
56 const int n = static_cast<int>(norm_coeffs.size()) - 1;
57 double root_scale_estimate = 1.0;
58 for (int i = 1; i <= n; ++i) {
59 const double magnitude = std::abs(norm_coeffs[i]);
60 if (magnitude > 0.0) {
61 root_scale_estimate = std::max(
62 root_scale_estimate,
63 std::exp(std::log(magnitude) / static_cast<double>(i)));
64 }
65 }
66 if (!std::isfinite(root_scale_estimate)) {
69 "characteristic-root variable scaling is non-finite",
70 operation);
71 }
72 int root_scale_exponent = std::ilogb(root_scale_estimate);
73 double root_scale = std::scalbn(1.0, root_scale_exponent);
74 if (root_scale_exponent < std::numeric_limits<double>::max_exponent - 1 &&
75 root_scale_estimate / root_scale > std::sqrt(2.0)) {
76 root_scale *= 2.0;
77 }
78
79 return root_scale;
80}
81
83 std::vector<CharRoot> scaled_roots, double root_scale,
84 const std::string& operation) {
85 for (auto& root : scaled_roots) {
86 root.real_part *= root_scale;
87 root.imag_part *= root_scale;
88 if (!std::isfinite(root.real_part) ||
89 !std::isfinite(root.imag_part)) {
90 return Result<std::vector<CharRoot>>::failure(
92 "characteristic root exceeds the finite numeric range",
93 operation);
94 }
95 }
96 return Result<std::vector<CharRoot>>::success(
97 std::move(scaled_roots));
98
99}
100
102 std::vector<CharRoot>& distinct_roots, const RealPolynomial& solver_coeffs) {
103 RealPolynomial remaining = solver_coeffs;
104 int recovered_degree = 0;
105 bool recovered_all = !distinct_roots.empty();
106 for (auto& root : distinct_roots) {
107 RealPolynomial factor;
108 if (root.is_complex) {
109 const double magnitude_squared =
110 root.real_part * root.real_part +
111 root.imag_part * root.imag_part;
112 if (!std::isfinite(magnitude_squared)) {
113 recovered_all = false;
114 break;
115 }
116 factor = {
117 1.0, -2.0 * root.real_part, magnitude_squared};
118 } else {
119 factor = {1.0, -root.real_part};
120 }
121
122 int multiplicity = 0;
123 while (remaining.size() >= factor.size()) {
124 auto division = divide_polynomials(remaining, factor);
126 division.second, remaining)) {
127 break;
128 }
129 remaining = std::move(division.first);
130 ++multiplicity;
131 }
132 if (multiplicity == 0) {
133 recovered_all = false;
134 break;
135 }
136 root.multiplicity = multiplicity;
137 recovered_degree +=
138 multiplicity * (root.is_complex ? 2 : 1);
139 }
140
141 return recovered_all && recovered_degree ==
142 static_cast<int>(solver_coeffs.size()) - 1;
143}
144
146 const RealPolynomial& solver_coeffs, const std::string& operation) {
148 solver_coeffs, polynomial_derivative(solver_coeffs));
149 if (repeated_factor.size() > 1 &&
150 repeated_factor.size() < solver_coeffs.size()) {
151 auto square_free_division =
152 divide_polynomials(solver_coeffs, repeated_factor);
154 square_free_division.second, solver_coeffs)) {
155 auto distinct_result = find_characteristic_roots(
156 square_free_division.first, operation);
157 if (!distinct_result) {
158 return Result<std::vector<CharRoot>>::failure(
159 distinct_result.error());
160 }
161 auto distinct_roots = std::move(distinct_result.value());
162
163 if (recover_root_multiplicities(distinct_roots, solver_coeffs)) {
164 return distinct_roots;
165 }
166 }
167 }
168 return std::vector<CharRoot>{};
169}
170
172 const RealPolynomial& solver_coeffs, const std::string& operation) {
173 const int n = static_cast<int>(solver_coeffs.size()) - 1;
174 std::vector<Complex> z(n);
175 /// 初始猜测采用不同半径,提供非对称起点以区分各根.
176 for (int i = 0; i < n; ++i) {
177 double angle = 2.0 * 3.14159265358979323846 * i / n + 0.1;
178 double radius = 1.0 + 0.3 * i;
179 z[i] = {radius * std::cos(angle), radius * std::sin(angle)};
180 }
181
182 /// 求值多项式 p(z)
183 auto eval_poly = [&](const Complex& val) -> Complex {
184 Complex result = {1.0, 0.0};
185 for (int i = 1; i <= n; ++i) {
186 result = result * val + Complex{solver_coeffs[i], 0.0};
187 }
188 return result;
189 };
190
191 /// Durand-Kerner 迭代
192 bool converged = false;
193 for (int iter = 0; iter < 1000; ++iter) {
194 double max_change = 0.0;
195 for (int i = 0; i < n; ++i) {
196 Complex num = eval_poly(z[i]);
197 Complex denom = {1.0, 0.0};
198 for (int j = 0; j < n; ++j) {
199 if (j != i) {
200 denom = denom * (z[i] - z[j]);
201 }
202 }
203 Complex delta = num / denom;
204 double change = delta.magnitude();
205 if (!std::isfinite(change)) {
206 return Result<std::vector<Complex>>::failure(
208 "characteristic-root iteration became non-finite",
209 operation);
210 }
211 z[i] = z[i] - delta;
212 if (change > max_change) {
213 max_change = change;
214 }
215 }
216 if (max_change < 1e-12) {
217 converged = true;
218 break;
219 }
220 }
221 if (!converged) {
222 return Result<std::vector<Complex>>::failure(
224 "characteristic-root iteration did not converge", operation);
225 }
226 for (const auto& candidate : z) {
227 if (polynomial_root_backward_error(solver_coeffs, candidate) >
229 return Result<std::vector<Complex>>::failure(
231 "characteristic root failed backward-error verification",
232 operation);
233 }
234 }
235
236 return z;
237}
238
239int conjugate_root_index(const std::vector<Complex>& z,
240 const std::vector<bool>& used, int i) {
241 const int n = static_cast<int>(z.size());
242 int conjugate_index = -1;
243 double best_distance = std::numeric_limits<double>::infinity();
244 for (int j = i + 1; j < n; ++j) {
245 if (used[j] || std::signbit(z[i].im) == std::signbit(z[j].im)) {
246 continue;
247 }
248 const double scale =
249 1.0 + std::max(z[i].magnitude(), z[j].magnitude());
250 const double distance =
251 std::hypot(z[i].re - z[j].re, z[i].im + z[j].im) /
252 scale;
253 if (distance < best_distance) {
254 best_distance = distance;
255 conjugate_index = j;
256 }
257 }
258
259 return conjugate_index;
260}
261
263 const RealPolynomial& solver_coeffs, const std::vector<Complex>& z,
264 const std::string& operation) {
265 const int n = static_cast<int>(z.size());
266 const double roundoff_tolerance =
267 256.0 * std::numeric_limits<double>::epsilon() *
268 static_cast<double>(std::max(n, 1));
269 std::vector<CharRoot> roots;
270 /// 使用原多项式验证实轴投影;固定虚部容差会吞掉尺度较小的复根。
271 /// 重数只由前面的多项式 GCD/因子恢复确定,不能按根间距离猜测。
272 std::vector<bool> used(n, false);
273 for (int i = 0; i < n; ++i) {
274 if (used[i]) {
275 continue;
276 }
277
278 const Complex real_projection{z[i].re, 0.0};
279 const double candidate_error =
280 polynomial_root_backward_error(solver_coeffs, z[i]);
281 const double projection_error =
282 polynomial_root_backward_error(solver_coeffs, real_projection);
283 const double projection_tolerance =
284 std::max(roundoff_tolerance, 16.0 * candidate_error);
285 if (projection_error <= projection_tolerance) {
286 used[i] = true;
287 roots.push_back({z[i].re, 0.0, 1, false});
288 continue;
289 }
290
291
292 const int conjugate_index = conjugate_root_index(z, used, i);
293 if (conjugate_index < 0) {
294 return Result<std::vector<CharRoot>>::failure(
296 "characteristic-root classification found an unpaired complex root",
297 operation);
298 }
299
300 const Complex paired_root{
301 0.5 * (z[i].re + z[conjugate_index].re),
302 0.5 * std::abs(z[i].im - z[conjugate_index].im)};
304 solver_coeffs, paired_root) > root_verification_tolerance) {
305 return Result<std::vector<CharRoot>>::failure(
307 "conjugate characteristic-root pair failed verification",
308 operation);
309 }
310 used[i] = true;
311 used[conjugate_index] = true;
312 roots.push_back(
313 {paired_root.re, paired_root.im, 1, true});
314 }
315
316 int classified_degree = 0;
317 for (const auto& root : roots) {
318 classified_degree += root.multiplicity * (root.is_complex ? 2 : 1);
319 }
320 if (classified_degree != n) {
321 return Result<std::vector<CharRoot>>::failure(
323 "characteristic-root classification lost roots", operation);
324 }
325
326 return roots;
327}
328}
329
330/**
331 * @internal
332 * @brief 使用数值方法求解特征多项式的所有根.
333 *
334 * 对于一、二次多项式使用解析公式;对于三至六次多项式使用
335 * Durand-Kerner 迭代。迭代前通过多项式与其导数的近似 GCD 识别并提取重根。
336 */
339 const std::vector<double>& coeffs, const std::string& operation)
340{
341 int n = static_cast<int>(coeffs.size()) - 1;
342 if (n <= 0) {
343 return Result<std::vector<CharRoot>>::failure(
345 "characteristic polynomial has no roots", operation);
346 }
347
348 std::vector<CharRoot> roots;
349 double leading = coeffs[0];
350 if (leading == 0.0) {
351 return Result<std::vector<CharRoot>>::failure(
353 "characteristic polynomial has no usable leading coefficient",
354 operation);
355 }
356 std::vector<double> norm_coeffs(coeffs.size());
357 for (size_t i = 0; i < coeffs.size(); ++i) {
358 norm_coeffs[i] = coeffs[i] / leading;
359 if (!std::isfinite(norm_coeffs[i])) {
360 return Result<std::vector<CharRoot>>::failure(
362 "characteristic polynomial normalization is non-finite",
363 operation);
364 }
365 }
366
367 if (n == 1) {
368 roots.push_back({-norm_coeffs[1], 0.0, 1, false});
369 return roots;
370 }
371 if (n == 2) {
372 return quadratic_characteristic_roots(norm_coeffs, operation);
373 }
374 auto scale_result = characteristic_root_scale(norm_coeffs, operation);
375 if (!scale_result) {
376 return Result<std::vector<CharRoot>>::failure(scale_result.error());
377 }
378 const double root_scale = scale_result.value();
379 std::vector<double> solver_coeffs = norm_coeffs;
380 for (int i = 1; i <= n; ++i) {
381 for (int power = 0; power < i; ++power) {
382 solver_coeffs[i] /= root_scale;
383 }
384 }
385
386 auto repeated = repeated_characteristic_roots(solver_coeffs, operation);
387 if (!repeated) {
388 return repeated;
389 }
390 if (!repeated.value().empty()) {
391 return restore_root_scale(std::move(repeated.value()), root_scale, operation);
392 }
393 auto candidates = iterate_characteristic_roots(solver_coeffs, operation);
394 if (!candidates) {
395 return Result<std::vector<CharRoot>>::failure(candidates.error());
396 }
397 auto classified = classify_characteristic_roots(
398 solver_coeffs, candidates.value(), operation);
399 if (!classified) {
400 return classified;
401 }
402 return restore_root_scale(std::move(classified.value()), root_scale, operation);
403}
404
405}
static Result failure(CasErrc code, std::string message, std::string operation={})
Result< std::vector< CharRoot > > quadratic_characteristic_roots(const RealPolynomial &norm_coeffs, const std::string &operation)
Result< std::vector< CharRoot > > classify_characteristic_roots(const RealPolynomial &solver_coeffs, const std::vector< Complex > &z, const std::string &operation)
int conjugate_root_index(const std::vector< Complex > &z, const std::vector< bool > &used, int i)
bool recover_root_multiplicities(std::vector< CharRoot > &distinct_roots, const RealPolynomial &solver_coeffs)
Result< std::vector< Complex > > iterate_characteristic_roots(const RealPolynomial &solver_coeffs, const std::string &operation)
Result< double > characteristic_root_scale(const RealPolynomial &norm_coeffs, const std::string &operation)
Result< std::vector< CharRoot > > repeated_characteristic_roots(const RealPolynomial &solver_coeffs, const std::string &operation)
Result< std::vector< CharRoot > > restore_root_scale(std::vector< CharRoot > scaled_roots, double root_scale, const std::string &operation)
std::pair< RealPolynomial, RealPolynomial > divide_polynomials(const RealPolynomial &dividend, const RealPolynomial &divisor)
RealPolynomial polynomial_derivative(const RealPolynomial &polynomial)
double polynomial_root_backward_error(const RealPolynomial &coefficients, const Complex &root)
Result< std::vector< CharRoot > > find_characteristic_roots(const std::vector< double > &coefficients, const std::string &operation)
使用数值方法求解特征多项式的所有根.
RealPolynomial approximate_polynomial_gcd(RealPolynomial lhs, RealPolynomial rhs)
bool polynomial_remainder_is_small(const RealPolynomial &remainder, const RealPolynomial &dividend)
std::shared_ptr< SymbolicExpr > num(int value)
ExprResult e()
const VectorField const VectorField & c
const MultiPoly & b
ExprSetResult roots(const ExprPtr &expression, const std::string &variable, ComputationContext &context, const SolveOptions &options)