BmnRoot
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,
2 Darmstadt 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 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 } else { // real roots
34 sq = sqrt(disc);
35 wr[m - 2] = fabs(b2) + sq;
36 if (b2 < 0.0)
37 wr[m - 2] = -wr[m - 2];
38 if (wr[m - 2] == 0)
39 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 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 float *c, dn, dr, ds, drn, dsn, eps, r, s;
87 int i;
88
89 c = new float[n + 1];
90 c[0] = 1.0;
91 r = quad[1];
92 s = quad[0];
93 eps = 1e-15;
94 *iter = 1;
95
96 do {
97 if (*iter > maxiter)
98 break;
99 if (((*iter) % 200) == 0) {
100 eps *= 10.0;
101 }
102 b[1] = a[1] - r;
103 c[1] = b[1] - r;
104
105 for (i = 2; i <= n; i++) {
106 b[i] = a[i] - r * b[i - 1] - s * b[i - 2];
107 c[i] = b[i] - r * c[i - 1] - s * c[i - 2];
108 }
109 dn = c[n - 1] * c[n - 3] - c[n - 2] * c[n - 2];
110 drn = b[n] * c[n - 3] - b[n - 1] * c[n - 2];
111 dsn = b[n - 1] * c[n - 1] - b[n] * c[n - 2];
112
113 if (fabs(dn) < 1e-10) {
114 if (dn < 0.0)
115 dn = -1e-8;
116 else
117 dn = 1e-8;
118 }
119 dr = drn / dn;
120 ds = dsn / dn;
121 r += dr;
122 s += ds;
123 (*iter)++;
124 } while ((fabs(dr) + fabs(ds)) > eps);
125 quad[0] = s;
126 quad[1] = r;
127 *err = fabs(ds) + fabs(dr);
128 delete[] c;
129}
130//
131// Differentiate polynomial 'a' returning result in 'b'.
132//
133void diff_poly(float *a, int n, float *b) {
134 float coef;
135 int i;
136
137 coef = (float)n;
138 b[0] = 1.0;
139 for (i = 1; i < n; i++) {
140 b[i] = a[i] * ((float)(n - i)) / coef;
141 }
142}
143//
144// Attempt to find a reliable estimate of a quadratic factor using modified
145// Bairstow's method with provisions for 'digging out' factors associated
146// with multiple roots.
147//
148// This resursive routine operates on the principal that differentiation of
149// a polynomial reduces the order of all multiple roots by one, and has no
150// other roots in common with it. If a root of the differentiated polynomial
151// is a root of the original polynomial, there must be multiple roots at
152// that location. The differentiated polynomial, however, has lower order
153// and is easier to solve.
154//
155// When the original polynomial exhibits convergence problems in the
156// neighborhood of some potential root, a best guess is obtained and tried
157// on the differentiated polynomial. The new best guess is applied
158// recursively on continually differentiated polynomials until failure
159// occurs. At this point, the previous polynomial is accepted as that with
160// the least number of roots at this location, and its estimate is
161// accepted as the root.
162//
163void recurse(float *a, int n, float *b, int m, float *quad, float *err,
164 int *iter) {
165 float *c, *x, rs[2], tst;
166
167 if (fabs(b[m]) < 1e-16)
168 m--; // this bypasses roots at zero
169 if (m == 2) {
170 quad[0] = b[2];
171 quad[1] = b[1];
172 *err = 0;
173 *iter = 0;
174 return;
175 }
176 c = new float[m + 1];
177 x = new float[n + 1];
178 c[0] = x[0] = 1.0;
179 rs[0] = quad[0];
180 rs[1] = quad[1];
181 *iter = 0;
182 find_quad(b, m, c, rs, err, iter);
183 tst = fabs(rs[0] - quad[0]) + fabs(rs[1] - quad[1]);
184 if (*err < 1e-12) {
185 quad[0] = rs[0];
186 quad[1] = rs[1];
187 }
188 // tst will be 'large' if we converge to wrong root
189 if (((*iter > 5) && (tst < 1e-4)) || ((*iter > 20) && (tst < 1e-1))) {
190 diff_poly(b, m, c);
191 recurse(a, n, c, m - 1, rs, err, iter);
192 quad[0] = rs[0];
193 quad[1] = rs[1];
194 }
195 delete[] x;
196 delete[] c;
197}
198//
199// Top level routine to manage the determination of all roots of the given
200// polynomial 'a', returning the quadratic factors (and possibly one linear
201// factor) in 'x'.
202//
203void get_quads(float *a, int n, float *quad, float *x) {
204 float *b, *z, err, tmp;
205 int iter, i, m;
206
207 if ((tmp = a[0]) != 1.0) {
208 a[0] = 1.0;
209 for (i = 1; i <= n; i++) {
210 a[i] /= tmp;
211 }
212 }
213 if (n == 2) {
214 x[0] = a[1];
215 x[1] = a[2];
216 return;
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)
243 break;
244 quad[0] = 9999.;
245 quad[1] = 9999.;
246 }
247 if (err > 0.01) {
248 printf("Error! Convergence failure in quadratic x^2 + r*x + s.\n");
249 exit(1);
250 }
251 } while (err > 0.01);
252 x[m - 2] = quad[1];
253 x[m - 1] = quad[0];
254 m -= 2;
255 for (i = 0; i <= m; i++) {
256 z[i] = b[i];
257 }
258 } while (m > 2);
259 if (m == 2) {
260 x[0] = b[1];
261 x[1] = b[2];
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 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 F32vec4 sqrt(const F32vec4 &a)
Definition P4_F32vec4.h:34
friend F32vec4 fabs(const F32vec4 &a)
Definition P4_F32vec4.h:52
int i
Definition P4_F32vec4.h:22
__m128 m
Definition P4_F32vec4.h:27
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)