BmnRoot 26.10.0-18-g4e3414f9
Software framework of the BM@N experiment
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#include <math.h>
8#include <stdlib.h>
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 } 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{
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)
100 break;
101 if (((*iter) % 200) == 0) {
102 eps *= 10.0;
103 }
104 b[1] = a[1] - r;
105 c[1] = b[1] - r;
106
107 for (i = 2; i <= n; i++) {
108 b[i] = a[i] - r * b[i - 1] - s * b[i - 2];
109 c[i] = b[i] - r * c[i - 1] - s * c[i - 2];
110 }
111 dn = c[n - 1] * c[n - 3] - c[n - 2] * c[n - 2];
112 drn = b[n] * c[n - 3] - b[n - 1] * c[n - 2];
113 dsn = b[n - 1] * c[n - 1] - b[n] * c[n - 2];
114
115 if (fabs(dn) < 1e-10) {
116 if (dn < 0.0)
117 dn = -1e-8;
118 else
119 dn = 1e-8;
120 }
121 dr = drn / dn;
122 ds = dsn / dn;
123 r += dr;
124 s += ds;
125 (*iter)++;
126 } while ((fabs(dr) + fabs(ds)) > eps);
127 quad[0] = s;
128 quad[1] = r;
129 *err = fabs(ds) + fabs(dr);
130 delete[] c;
131}
132//
133// Differentiate polynomial 'a' returning result in 'b'.
134//
135void diff_poly(float* a, int n, float* b)
136{
137 float coef;
138 int i;
139
140 coef = (float)n;
141 b[0] = 1.0;
142 for (i = 1; i < n; i++) {
143 b[i] = a[i] * ((float)(n - i)) / coef;
144 }
145}
146//
147// Attempt to find a reliable estimate of a quadratic factor using modified
148// Bairstow's method with provisions for 'digging out' factors associated
149// with multiple roots.
150//
151// This resursive routine operates on the principal that differentiation of
152// a polynomial reduces the order of all multiple roots by one, and has no
153// other roots in common with it. If a root of the differentiated polynomial
154// is a root of the original polynomial, there must be multiple roots at
155// that location. The differentiated polynomial, however, has lower order
156// and is easier to solve.
157//
158// When the original polynomial exhibits convergence problems in the
159// neighborhood of some potential root, a best guess is obtained and tried
160// on the differentiated polynomial. The new best guess is applied
161// recursively on continually differentiated polynomials until failure
162// occurs. At this point, the previous polynomial is accepted as that with
163// the least number of roots at this location, and its estimate is
164// accepted as the root.
165//
166void recurse(float* a, int n, float* b, int m, float* quad, float* err, int* iter)
167{
168 float *c, *x, rs[2], tst;
169
170 if (fabs(b[m]) < 1e-16)
171 m--; // this bypasses roots at zero
172 if (m == 2) {
173 quad[0] = b[2];
174 quad[1] = b[1];
175 *err = 0;
176 *iter = 0;
177 return;
178 }
179 c = new float[m + 1];
180 x = new float[n + 1];
181 c[0] = x[0] = 1.0;
182 rs[0] = quad[0];
183 rs[1] = quad[1];
184 *iter = 0;
185 find_quad(b, m, c, rs, err, iter);
186 tst = fabs(rs[0] - quad[0]) + fabs(rs[1] - quad[1]);
187 if (*err < 1e-12) {
188 quad[0] = rs[0];
189 quad[1] = rs[1];
190 }
191 // tst will be 'large' if we converge to wrong root
192 if (((*iter > 5) && (tst < 1e-4)) || ((*iter > 20) && (tst < 1e-1))) {
193 diff_poly(b, m, c);
194 recurse(a, n, c, m - 1, rs, err, iter);
195 quad[0] = rs[0];
196 quad[1] = rs[1];
197 }
198 delete[] x;
199 delete[] c;
200}
201//
202// Top level routine to manage the determination of all roots of the given
203// polynomial 'a', returning the quadratic factors (and possibly one linear
204// factor) in 'x'.
205//
206void get_quads(float* a, int n, float* quad, float* x)
207{
208 float *b, *z, err, tmp;
209 int iter, i, m;
210
211 if ((tmp = a[0]) != 1.0) {
212 a[0] = 1.0;
213 for (i = 1; i <= n; i++) {
214 a[i] /= tmp;
215 }
216 }
217 if (n == 2) {
218 x[0] = a[1];
219 x[1] = a[2];
220 return;
221 } else if (n == 1) {
222 x[0] = a[1];
223 return;
224 }
225 m = n;
226 b = new float[n + 1];
227 z = new float[n + 1];
228 b[0] = 1.0;
229 for (i = 0; i <= n; i++) {
230 z[i] = a[i];
231 x[i] = 0.0;
232 }
233 do {
234 if (n > m) {
235 quad[0] = 3.14159e-1;
236 quad[1] = 2.78127e-1;
237 }
238 do { // This loop tries to assure convergence
239 for (i = 0; i < 5; i++) {
240 find_quad(z, m, b, quad, &err, &iter);
241 if ((err > 1e-7) || (iter > maxiter)) {
242 diff_poly(z, m, b);
243 recurse(z, m, b, m - 1, quad, &err, &iter);
244 }
245 deflate(z, m, b, quad, &err);
246 if (err < 0.001)
247 break;
248 quad[0] = 9999.;
249 quad[1] = 9999.;
250 }
251 if (err > 0.01) {
252 printf("Error! Convergence failure in quadratic x^2 + r*x + s.\n");
253 exit(1);
254 }
255 } while (err > 0.01);
256 x[m - 2] = quad[1];
257 x[m - 1] = quad[0];
258 m -= 2;
259 for (i = 0; i <= m; i++) {
260 z[i] = b[i];
261 }
262 } while (m > 2);
263 if (m == 2) {
264 x[0] = b[1];
265 x[1] = b[2];
266 } else
267 x[0] = b[1];
268 delete[] z;
269 delete[] b;
270}
271
272void polynomComplexRoots(float* wr, float* wi, int n, float* a, int& numr)
273{
274 float* quad = new float[2];
275 float* x = new float[n];
276
277 // initialize estimate for 1st root pair
278 quad[0] = 2.71828e-1;
279 quad[1] = 3.14159e-1;
280
281 // get roots
282 get_quads(a, n, quad, x);
283 numr = roots(x, n, wr, wi);
284
285 delete[] quad;
286 delete[] x;
287}
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)
RVec< PromoteType< T > > sqrt(const RVec< T > &v)
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:20362