3#include "lmmc/numeric.h"
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);
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});
37 {-
b / 2.0, scale * std::sqrt(-discriminant) / 2.0,
40 for (
const auto& root :
roots) {
42 norm_coeffs, {root.real_part, root.imag_part}) >
46 "quadratic characteristic root failed verification",
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(
63 std::exp(std::log(magnitude) /
static_cast<double>(i)));
66 if (!std::isfinite(root_scale_estimate)) {
69 "characteristic-root variable scaling is non-finite",
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)) {
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)) {
92 "characteristic root exceeds the finite numeric range",
97 std::move(scaled_roots));
102 std::vector<CharRoot>& distinct_roots,
const RealPolynomial& solver_coeffs) {
104 int recovered_degree = 0;
105 bool recovered_all = !distinct_roots.empty();
106 for (
auto& root : distinct_roots) {
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;
117 1.0, -2.0 * root.real_part, magnitude_squared};
119 factor = {1.0, -root.real_part};
122 int multiplicity = 0;
123 while (remaining.size() >= factor.size()) {
126 division.second, remaining)) {
129 remaining = std::move(division.first);
132 if (multiplicity == 0) {
133 recovered_all =
false;
136 root.multiplicity = multiplicity;
138 multiplicity * (root.is_complex ? 2 : 1);
141 return recovered_all && recovered_degree ==
142 static_cast<int>(solver_coeffs.size()) - 1;
146 const RealPolynomial& solver_coeffs,
const std::string& operation) {
149 if (repeated_factor.size() > 1 &&
150 repeated_factor.size() < solver_coeffs.size()) {
151 auto square_free_division =
154 square_free_division.second, solver_coeffs)) {
156 square_free_division.first, operation);
157 if (!distinct_result) {
159 distinct_result.error());
161 auto distinct_roots = std::move(distinct_result.value());
164 return distinct_roots;
168 return std::vector<CharRoot>{};
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);
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)};
185 for (
int i = 1; i <= n; ++i) {
186 result = result * val +
Complex{solver_coeffs[i], 0.0};
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) {
198 for (
int j = 0; j < n; ++j) {
200 denom = denom * (z[i] - z[j]);
205 if (!std::isfinite(change)) {
208 "characteristic-root iteration became non-finite",
212 if (change > max_change) {
216 if (max_change < 1
e-12) {
224 "characteristic-root iteration did not converge", operation);
226 for (
const auto& candidate : z) {
231 "characteristic root failed backward-error verification",
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)) {
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) /
253 if (distance < best_distance) {
254 best_distance = distance;
259 return conjugate_index;
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;
272 std::vector<bool> used(n,
false);
273 for (
int i = 0; i < n; ++i) {
278 const Complex real_projection{z[i].re, 0.0};
279 const double candidate_error =
281 const double projection_error =
283 const double projection_tolerance =
284 std::max(roundoff_tolerance, 16.0 * candidate_error);
285 if (projection_error <= projection_tolerance) {
287 roots.push_back({z[i].re, 0.0, 1,
false});
293 if (conjugate_index < 0) {
296 "characteristic-root classification found an unpaired complex root",
301 0.5 * (z[i].re + z[conjugate_index].re),
302 0.5 * std::abs(z[i].im - z[conjugate_index].im)};
307 "conjugate characteristic-root pair failed verification",
311 used[conjugate_index] =
true;
313 {paired_root.
re, paired_root.
im, 1,
true});
316 int classified_degree = 0;
317 for (
const auto& root :
roots) {
318 classified_degree += root.multiplicity * (root.is_complex ? 2 : 1);
320 if (classified_degree != n) {
323 "characteristic-root classification lost roots", operation);
339 const std::vector<double>& coeffs,
const std::string& operation)
341 int n =
static_cast<int>(coeffs.size()) - 1;
345 "characteristic polynomial has no roots", operation);
348 std::vector<CharRoot>
roots;
349 double leading = coeffs[0];
350 if (leading == 0.0) {
353 "characteristic polynomial has no usable leading coefficient",
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])) {
362 "characteristic polynomial normalization is non-finite",
368 roots.push_back({-norm_coeffs[1], 0.0, 1,
false});
372 return quadratic_characteristic_roots(norm_coeffs, operation);
374 auto scale_result = characteristic_root_scale(norm_coeffs, operation);
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;
386 auto repeated = repeated_characteristic_roots(solver_coeffs, operation);
390 if (!repeated.value().empty()) {
391 return restore_root_scale(std::move(repeated.value()), root_scale, operation);
393 auto candidates = iterate_characteristic_roots(solver_coeffs, operation);
397 auto classified = classify_characteristic_roots(
398 solver_coeffs, candidates.value(), operation);
402 return restore_root_scale(std::move(classified.value()), root_scale, operation);
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)
constexpr double root_verification_tolerance
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 ÷nd, 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)
std::vector< double > RealPolynomial
bool polynomial_remainder_is_small(const RealPolynomial &remainder, const RealPolynomial ÷nd)
std::shared_ptr< SymbolicExpr > num(int value)
const VectorField const VectorField & c
ExprSetResult roots(const ExprPtr &expression, const std::string &variable, ComputationContext &context, const SolveOptions &options)