CbmRoot
Loading...
Searching...
No Matches
PolynomComplexRoots.h
Go to the documentation of this file.
1/* Copyright (C) 2020 Facility for Antiproton and Ion Research in Europe, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Pierre-Alain Loizeau [committer] */
4
5#include <cmath>
6#include <cstdlib>
7#include <iomanip>
8#include <iostream>
9
10#define maxiter 500
11
12//
13// Extract individual real or complex roots from list of quadratic factors
14//
15int roots(float* a, int n, float* wr, float* wi)
16{
17 float sq, b2, c, disc;
18 int m, numroots;
19
20 m = n;
21 numroots = 0;
22 while (m > 1) {
23 b2 = -0.5 * a[m - 2];
24 c = a[m - 1];
25 disc = b2 * b2 - c;
26 if (disc < 0.0) { // complex roots
27 sq = sqrt(-disc);
28 wr[m - 2] = b2;
29 wi[m - 2] = sq;
30 wr[m - 1] = b2;
31 wi[m - 1] = -sq;
32 numroots += 2;
33 }
34 else { // real roots
35 sq = sqrt(disc);
36 wr[m - 2] = fabs(b2) + sq;
37 if (b2 < 0.0) wr[m - 2] = -wr[m - 2];
38 if (wr[m - 2] == 0) wr[m - 1] = 0;
39 else {
40 wr[m - 1] = c / wr[m - 2];
41 numroots += 2;
42 }
43 wi[m - 2] = 0.0;
44 wi[m - 1] = 0.0;
45 }
46 m -= 2;
47 }
48 if (m == 1) {
49 wr[0] = -a[0];
50 wi[0] = 0.0;
51 numroots++;
52 }
53 return numroots;
54}
55//
56// Deflate polynomial 'a' by dividing out 'quad'. Return quotient
57// polynomial in 'b' and error metric based on remainder in 'err'.
58//
59void deflate(float* a, int n, float* b, float* quad, float* err)
60{
61 float r, s;
62 int i;
63
64 r = quad[1];
65 s = quad[0];
66
67 b[1] = a[1] - r;
68
69 for (i = 2; i <= n; i++) {
70 b[i] = a[i] - r * b[i - 1] - s * b[i - 2];
71 }
72 *err = fabs(b[n]) + fabs(b[n - 1]);
73}
74//
75// Find quadratic factor using Bairstow's method (quadratic Newton method).
76// A number of ad hoc safeguards are incorporated to prevent stalls due
77// to common difficulties, such as zero slope at iteration point, and
78// convergence problems.
79//
80// Bairstow's method is sensitive to the starting estimate. It is possible
81// for convergence to fail or for 'wild' values to trigger an overflow.
82//
83// It is advisable to institute traps for these problems. (To do!)
84//
85void find_quad(float* a, int n, float* b, float* quad, float* err, int* iter)
86{
87 float *c, dn, dr, ds, drn, dsn, eps, r, s;
88 int i;
89
90 c = new float[n + 1];
91 c[0] = 1.0;
92 r = quad[1];
93 s = quad[0];
94 eps = 1e-15;
95 *iter = 1;
96
97 do {
98 if (*iter > maxiter) break;
99 if (((*iter) % 200) == 0) { eps *= 10.0; }
100 b[1] = a[1] - r;
101 c[1] = b[1] - r;
102
103 for (i = 2; i <= n; i++) {
104 b[i] = a[i] - r * b[i - 1] - s * b[i - 2];
105 c[i] = b[i] - r * c[i - 1] - s * c[i - 2];
106 }
107 dn = c[n - 1] * c[n - 3] - c[n - 2] * c[n - 2];
108 drn = b[n] * c[n - 3] - b[n - 1] * c[n - 2];
109 dsn = b[n - 1] * c[n - 1] - b[n] * c[n - 2];
110
111 if (fabs(dn) < 1e-10) {
112 if (dn < 0.0) dn = -1e-8;
113 else
114 dn = 1e-8;
115 }
116 dr = drn / dn;
117 ds = dsn / dn;
118 r += dr;
119 s += ds;
120 (*iter)++;
121 } while ((fabs(dr) + fabs(ds)) > eps);
122 quad[0] = s;
123 quad[1] = r;
124 *err = fabs(ds) + fabs(dr);
125 delete[] c;
126}
127//
128// Differentiate polynomial 'a' returning result in 'b'.
129//
130void diff_poly(float* a, int n, float* b)
131{
132 float coef;
133 int i;
134
135 coef = (float) n;
136 b[0] = 1.0;
137 for (i = 1; i < n; i++) {
138 b[i] = a[i] * ((float) (n - i)) / coef;
139 }
140}
141//
142// Attempt to find a reliable estimate of a quadratic factor using modified
143// Bairstow's method with provisions for 'digging out' factors associated
144// with multiple roots.
145//
146// This resursive routine operates on the principal that differentiation of
147// a polynomial reduces the order of all multiple roots by one, and has no
148// other roots in common with it. If a root of the differentiated polynomial
149// is a root of the original polynomial, there must be multiple roots at
150// that location. The differentiated polynomial, however, has lower order
151// and is easier to solve.
152//
153// When the original polynomial exhibits convergence problems in the
154// neighborhood of some potential root, a best guess is obtained and tried
155// on the differentiated polynomial. The new best guess is applied
156// recursively on continually differentiated polynomials until failure
157// occurs. At this point, the previous polynomial is accepted as that with
158// the least number of roots at this location, and its estimate is
159// accepted as the root.
160//
161void recurse(float* a, int n, float* b, int m, float* quad, float* err, int* iter)
162{
163 float *c, *x, rs[2], tst;
164
165 if (fabs(b[m]) < 1e-16) m--; // this bypasses roots at zero
166 if (m == 2) {
167 quad[0] = b[2];
168 quad[1] = b[1];
169 *err = 0;
170 *iter = 0;
171 return;
172 }
173 c = new float[m + 1];
174 x = new float[n + 1];
175 c[0] = x[0] = 1.0;
176 rs[0] = quad[0];
177 rs[1] = quad[1];
178 *iter = 0;
179 find_quad(b, m, c, rs, err, iter);
180 tst = fabs(rs[0] - quad[0]) + fabs(rs[1] - quad[1]);
181 if (*err < 1e-12) {
182 quad[0] = rs[0];
183 quad[1] = rs[1];
184 }
185 // tst will be 'large' if we converge to wrong root
186 if (((*iter > 5) && (tst < 1e-4)) || ((*iter > 20) && (tst < 1e-1))) {
187 diff_poly(b, m, c);
188 recurse(a, n, c, m - 1, rs, err, iter);
189 quad[0] = rs[0];
190 quad[1] = rs[1];
191 }
192 delete[] x;
193 delete[] c;
194}
195//
196// Top level routine to manage the determination of all roots of the given
197// polynomial 'a', returning the quadratic factors (and possibly one linear
198// factor) in 'x'.
199//
200void get_quads(float* a, int n, float* quad, float* x)
201{
202 float *b, *z, err, tmp;
203 int iter, i, m;
204
205 if ((tmp = a[0]) != 1.0) {
206 a[0] = 1.0;
207 for (i = 1; i <= n; i++) {
208 a[i] /= tmp;
209 }
210 }
211 if (n == 2) {
212 x[0] = a[1];
213 x[1] = a[2];
214 return;
215 }
216 else if (n == 1) {
217 x[0] = a[1];
218 return;
219 }
220 m = n;
221 b = new float[n + 1];
222 z = new float[n + 1];
223 b[0] = 1.0;
224 for (i = 0; i <= n; i++) {
225 z[i] = a[i];
226 x[i] = 0.0;
227 }
228 do {
229 if (n > m) {
230 quad[0] = 3.14159e-1;
231 quad[1] = 2.78127e-1;
232 }
233 do { // This loop tries to assure convergence
234 for (i = 0; i < 5; i++) {
235 find_quad(z, m, b, quad, &err, &iter);
236 if ((err > 1e-7) || (iter > maxiter)) {
237 diff_poly(z, m, b);
238 recurse(z, m, b, m - 1, quad, &err, &iter);
239 }
240 deflate(z, m, b, quad, &err);
241 if (err < 0.001) break;
242 quad[0] = 9999.;
243 quad[1] = 9999.;
244 }
245 if (err > 0.01) {
246 printf("Error! Convergence failure in quadratic x^2 + r*x + s.\n");
247 exit(1);
248 }
249 } while (err > 0.01);
250 x[m - 2] = quad[1];
251 x[m - 1] = quad[0];
252 m -= 2;
253 for (i = 0; i <= m; i++) {
254 z[i] = b[i];
255 }
256 } while (m > 2);
257 if (m == 2) {
258 x[0] = b[1];
259 x[1] = b[2];
260 }
261 else
262 x[0] = b[1];
263 delete[] z;
264 delete[] b;
265}
266
267void polynomComplexRoots(float* wr, float* wi, int n, float* a, int& numr)
268{
269 float* quad = new float[2];
270 float* x = new float[n];
271
272 // initialize estimate for 1st root pair
273 quad[0] = 2.71828e-1;
274 quad[1] = 3.14159e-1;
275
276 // get roots
277 get_quads(a, n, quad, x);
278 numr = roots(x, n, wr, wi);
279
280 delete[] quad;
281 delete[] x;
282}
friend fvec sqrt(const fvec &a)
void polynomComplexRoots(float *wr, float *wi, int n, float *a, int &numr)
int roots(float *a, int n, float *wr, float *wi)
void diff_poly(float *a, int n, float *b)
#define maxiter
void find_quad(float *a, int n, float *b, float *quad, float *err, int *iter)
void deflate(float *a, int n, float *b, float *quad, float *err)
void get_quads(float *a, int n, float *quad, float *x)
void recurse(float *a, int n, float *b, int m, float *quad, float *err, int *iter)