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