00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00020
00022
00036
00037
00039
00040 #include <iostream>
00041 #include <cmath>
00042 #include <stdexcept>
00043 #include "poly_roots.h"
00044
00045
00046
00047 #define MAT(m,i,j,n) ((m)[(i)*(n) + (j)])
00048
00049
00050 #define FMAT(m,i,j,n) ((m)[((i)-1)*(n) + ((j)-1)])
00051
00052
00053 #define GSL_DBL_EPSILON 2.2204460492503131e-16
00054
00055 #define RADIX 2
00056 #define RADIX2 (RADIX*RADIX)
00057
00058 #define GSL_SUCCESS 0
00059 #define GSL_FAILURE -1
00060 #define GSL_EINVAL 4
00061 #define GSL_EFAILED 5
00062
00063 #define GSL_SET_COMPLEX_PACKED(zp,n,x,y) do {*((zp)+2*(n))=(x); *((zp)+(2*(n)+1))=(y);} while(0)
00064
00065
00066 typedef double *gsl_complex_packed_ptr ;
00067
00068 typedef struct {
00069 size_t nc;
00070 double * matrix;
00071 } gsl_poly_complex_workspace ;
00072
00073
00074
00075
00076 static gsl_poly_complex_workspace *
00077 gsl_poly_complex_workspace_alloc (size_t n);
00078
00079 static void
00080 gsl_poly_complex_workspace_free (gsl_poly_complex_workspace * w);
00081
00082 static void
00083 set_companion_matrix (const double *a, size_t n, double *m);
00084
00085 static int
00086 qr_companion (double *h, size_t nc, gsl_complex_packed_ptr z);
00087
00088 static void
00089 balance_companion_matrix (double *m, size_t n);
00090
00091 static int
00092 gsl_poly_complex_solve (const double *a, size_t n,
00093 gsl_poly_complex_workspace * w,
00094 gsl_complex_packed_ptr z);
00095
00096
00097
00098
00099 int
00100 poly_root_solve (Matrix& roots, Vector& coeffs)
00101 {
00102 Index a;
00103 double *c;
00104 double *s;
00105
00106 a = coeffs.nelem();
00107
00108 assert (roots.nrows() == a - 1);
00109 assert (roots.ncols() == 2);
00110 assert (coeffs[a - 1] != 0);
00111
00112 #ifdef USE_DOUBLE
00113 c = coeffs.mdata;
00114 s = roots.mdata;
00115 #else
00116 c = (double *)malloc (a * sizeof (double));
00117 for (Index i = 0; i < a; i++)
00118 c[i] = (double)coeffs[i];
00119
00120 s = (double *)malloc ((a-1) * 2 * sizeof (double));
00121 #endif
00122
00123 gsl_poly_complex_workspace *w = gsl_poly_complex_workspace_alloc (a);
00124
00125 int status = gsl_poly_complex_solve (c, a, w, s);
00126
00127 #ifndef USE_DOUBLE
00128 if (status == GSL_SUCCESS)
00129 {
00130 for (Index i = 0; i < a - 1; i++)
00131 {
00132 roots (i, 0) = (Numeric)s[i * 2];
00133 roots (i, 1) = (Numeric)s[i * 2 + 1];
00134 }
00135 }
00136
00137 free (c);
00138 free (s);
00139 #endif
00140
00141 gsl_poly_complex_workspace_free (w);
00142
00143 return status;
00144 }
00145
00146
00147 static gsl_poly_complex_workspace *
00148 gsl_poly_complex_workspace_alloc (size_t n)
00149 {
00150 size_t nc ;
00151
00152 gsl_poly_complex_workspace * w ;
00153
00154 if (n == 0)
00155 {
00156 cerr << "matrix size n must be positive integer" << endl;
00157
00158 return (NULL);
00159 }
00160
00161 w = (gsl_poly_complex_workspace *)
00162 malloc (sizeof(gsl_poly_complex_workspace));
00163
00164 if (w == 0)
00165 {
00166 cerr << "failed to allocate space for struct" << endl;
00167
00168 return (NULL);
00169 }
00170
00171 nc = n - 1;
00172
00173 w->nc = nc;
00174
00175 w->matrix = (double *) malloc (nc * nc * sizeof(double));
00176
00177 if (w->matrix == 0)
00178 {
00179 free (w) ;
00180
00181 cerr << "failed to allocate space for workspace matrix" << endl;
00182
00183 return (NULL);
00184 }
00185
00186 return w ;
00187 }
00188
00189
00190 static void
00191 gsl_poly_complex_workspace_free (gsl_poly_complex_workspace * w)
00192 {
00193 free(w->matrix) ;
00194 free(w);
00195 }
00196
00197
00198 static void
00199 balance_companion_matrix (double *m, size_t nc)
00200 {
00201 int not_converged = 1;
00202
00203 double row_norm = 0;
00204 double col_norm = 0;
00205
00206 while (not_converged)
00207 {
00208 size_t i, j;
00209 double g, f, s;
00210
00211 not_converged = 0;
00212
00213 for (i = 0; i < nc; i++)
00214 {
00215
00216
00217 if (i != nc - 1)
00218 {
00219 col_norm = fabs (MAT (m, i + 1, i, nc));
00220 }
00221 else
00222 {
00223 col_norm = 0;
00224
00225 for (j = 0; j < nc - 1; j++)
00226 {
00227 col_norm += fabs (MAT (m, j, nc - 1, nc));
00228 }
00229 }
00230
00231
00232
00233 if (i == 0)
00234 {
00235 row_norm = fabs (MAT (m, 0, nc - 1, nc));
00236 }
00237 else if (i == nc - 1)
00238 {
00239 row_norm = fabs (MAT (m, i, i - 1, nc));
00240 }
00241 else
00242 {
00243 row_norm = (fabs (MAT (m, i, i - 1, nc))
00244 + fabs (MAT (m, i, nc - 1, nc)));
00245 }
00246
00247 if (col_norm == 0 || row_norm == 0)
00248 {
00249 continue;
00250 }
00251
00252 g = row_norm / RADIX;
00253 f = 1;
00254 s = col_norm + row_norm;
00255
00256 while (col_norm < g)
00257 {
00258 f *= RADIX;
00259 col_norm *= RADIX2;
00260 }
00261
00262 g = row_norm * RADIX;
00263
00264 while (col_norm > g)
00265 {
00266 f /= RADIX;
00267 col_norm /= RADIX2;
00268 }
00269
00270 if ((row_norm + col_norm) < 0.95 * s * f)
00271 {
00272 not_converged = 1;
00273
00274 g = 1 / f;
00275
00276 if (i == 0)
00277 {
00278 MAT (m, 0, nc - 1, nc) *= g;
00279 }
00280 else
00281 {
00282 MAT (m, i, i - 1, nc) *= g;
00283 MAT (m, i, nc - 1, nc) *= g;
00284 }
00285
00286 if (i == nc - 1)
00287 {
00288 for (j = 0; j < nc; j++)
00289 {
00290 MAT (m, j, i, nc) *= f;
00291 }
00292 }
00293 else
00294 {
00295 MAT (m, i + 1, i, nc) *= f;
00296 }
00297 }
00298 }
00299 }
00300 }
00301
00302
00303 static int
00304 qr_companion (double *h, size_t nc, gsl_complex_packed_ptr zroot)
00305 {
00306 double t = 0.0;
00307
00308 size_t iterations, e, i, j, k, m;
00309
00310 double w, x, y, s, z;
00311
00312 double p = 0, q = 0, r = 0;
00313
00314
00315
00316
00317
00318
00319 int notlast;
00320
00321 size_t n = nc;
00322
00323 next_root:
00324
00325 if (n == 0)
00326 return GSL_SUCCESS ;
00327
00328 iterations = 0;
00329
00330 next_iteration:
00331
00332 for (e = n; e >= 2; e--)
00333 {
00334 double a1 = fabs (FMAT (h, e, e - 1, nc));
00335 double a2 = fabs (FMAT (h, e - 1, e - 1, nc));
00336 double a3 = fabs (FMAT (h, e, e, nc));
00337
00338 if (a1 <= GSL_DBL_EPSILON * (a2 + a3))
00339 break;
00340 }
00341
00342 x = FMAT (h, n, n, nc);
00343
00344 if (e == n)
00345 {
00346 GSL_SET_COMPLEX_PACKED (zroot, n-1, x + t, 0);
00347 n--;
00348 goto next_root;
00349
00350 }
00351
00352 y = FMAT (h, n - 1, n - 1, nc);
00353 w = FMAT (h, n - 1, n, nc) * FMAT (h, n, n - 1, nc);
00354
00355 if (e == n - 1)
00356 {
00357 p = (y - x) / 2;
00358 q = p * p + w;
00359 y = sqrt (fabs (q));
00360
00361 x += t;
00362
00363 if (q > 0)
00364 {
00365 if (p < 0)
00366 y = -y;
00367 y += p;
00368
00369 GSL_SET_COMPLEX_PACKED (zroot, n-1, x - w / y, 0);
00370 GSL_SET_COMPLEX_PACKED (zroot, n-2, x + y, 0);
00371 }
00372 else
00373 {
00374 GSL_SET_COMPLEX_PACKED (zroot, n-1, x + p, -y);
00375 GSL_SET_COMPLEX_PACKED (zroot, n-2, x + p, y);
00376 }
00377 n -= 2;
00378
00379 goto next_root;
00380
00381 }
00382
00383
00384
00385 if (iterations == 60)
00386 {
00387
00388
00389 return GSL_FAILURE ;
00390 }
00391
00392 if (iterations % 10 == 0 && iterations > 0)
00393 {
00394
00395
00396 t += x;
00397 for (i = 1; i <= n; i++)
00398 {
00399 FMAT (h, i, i, nc) -= x;
00400 }
00401
00402 s = fabs (FMAT (h, n, n - 1, nc)) + fabs (FMAT (h, n - 1, n - 2, nc));
00403 y = 0.75 * s;
00404 x = y;
00405 w = -0.4375 * s * s;
00406 }
00407
00408 iterations++;
00409
00410 for (m = n - 2; m >= e; m--)
00411 {
00412 double a1, a2, a3;
00413
00414 z = FMAT (h, m, m, nc);
00415 r = x - z;
00416 s = y - z;
00417 p = FMAT (h, m, m + 1, nc) + (r * s - w) / FMAT (h, m + 1, m, nc);
00418 q = FMAT (h, m + 1, m + 1, nc) - z - r - s;
00419 r = FMAT (h, m + 2, m + 1, nc);
00420 s = fabs (p) + fabs (q) + fabs (r);
00421 p /= s;
00422 q /= s;
00423 r /= s;
00424
00425 if (m == e)
00426 break;
00427
00428 a1 = fabs (FMAT (h, m, m - 1, nc));
00429 a2 = fabs (FMAT (h, m - 1, m - 1, nc));
00430 a3 = fabs (FMAT (h, m + 1, m + 1, nc));
00431
00432 if (a1 * (fabs (q) + fabs (r)) <= GSL_DBL_EPSILON * fabs (p) * (a2 + a3))
00433 break;
00434 }
00435
00436 for (i = m + 2; i <= n; i++)
00437 {
00438 FMAT (h, i, i - 2, nc) = 0;
00439 }
00440
00441 for (i = m + 3; i <= n; i++)
00442 {
00443 FMAT (h, i, i - 3, nc) = 0;
00444 }
00445
00446
00447
00448 for (k = m; k <= n - 1; k++)
00449 {
00450 notlast = (k != n - 1);
00451
00452 if (k != m)
00453 {
00454 p = FMAT (h, k, k - 1, nc);
00455 q = FMAT (h, k + 1, k - 1, nc);
00456 r = notlast ? FMAT (h, k + 2, k - 1, nc) : 0.0;
00457
00458 x = fabs (p) + fabs (q) + fabs (r);
00459
00460 if (x == 0)
00461 continue;
00462
00463 p /= x;
00464 q /= x;
00465 r /= x;
00466 }
00467
00468 s = sqrt (p * p + q * q + r * r);
00469
00470 if (p < 0)
00471 s = -s;
00472
00473 if (k != m)
00474 {
00475 FMAT (h, k, k - 1, nc) = -s * x;
00476 }
00477 else if (e != m)
00478 {
00479 FMAT (h, k, k - 1, nc) *= -1;
00480 }
00481
00482 p += s;
00483 x = p / s;
00484 y = q / s;
00485 z = r / s;
00486 q /= p;
00487 r /= p;
00488
00489
00490
00491 for (j = k; j <= n; j++)
00492 {
00493 p = FMAT (h, k, j, nc) + q * FMAT (h, k + 1, j, nc);
00494
00495 if (notlast)
00496 {
00497 p += r * FMAT (h, k + 2, j, nc);
00498 FMAT (h, k + 2, j, nc) -= p * z;
00499 }
00500
00501 FMAT (h, k + 1, j, nc) -= p * y;
00502 FMAT (h, k, j, nc) -= p * x;
00503 }
00504
00505 j = (k + 3 < n) ? (k + 3) : n;
00506
00507
00508
00509 for (i = e; i <= j; i++)
00510 {
00511 p = x * FMAT (h, i, k, nc) + y * FMAT (h, i, k + 1, nc);
00512
00513 if (notlast)
00514 {
00515 p += z * FMAT (h, i, k + 2, nc);
00516 FMAT (h, i, k + 2, nc) -= p * r;
00517 }
00518 FMAT (h, i, k + 1, nc) -= p * q;
00519 FMAT (h, i, k, nc) -= p;
00520 }
00521 }
00522
00523 goto next_iteration;
00524 }
00525
00526
00527 static void
00528 set_companion_matrix (const double *a, size_t nc, double *m)
00529 {
00530 size_t i, j;
00531
00532 for (i = 0; i < nc; i++)
00533 for (j = 0; j < nc; j++)
00534 MAT (m, i, j, nc) = 0.0;
00535
00536 for (i = 1; i < nc; i++)
00537 MAT (m, i, i - 1, nc) = 1.0;
00538
00539 for (i = 0; i < nc; i++)
00540 MAT (m, i, nc - 1, nc) = -a[i] / a[nc];
00541 }
00542
00543
00544 static int
00545 gsl_poly_complex_solve (const double *a, size_t n,
00546 gsl_poly_complex_workspace * w,
00547 gsl_complex_packed_ptr z)
00548 {
00549 int status;
00550 double *m;
00551
00552 if (n == 0)
00553 {
00554 cerr << "number of terms must be a positive integer" << endl;
00555
00556 return (GSL_FAILURE);
00557 }
00558
00559 if (n == 1)
00560 {
00561 cerr << "cannot solve for only one term" << endl;
00562
00563 return (GSL_FAILURE);
00564 }
00565
00566 if (a[n - 1] == 0)
00567 {
00568 cerr << "leading term of polynomial must be non-zero" << endl;
00569
00570 return (GSL_FAILURE);
00571 }
00572
00573 if (w->nc != n - 1)
00574 {
00575 cerr << "size of workspace does not match polynomial" << endl;
00576
00577 return (GSL_FAILURE);
00578 }
00579
00580 m = w->matrix;
00581
00582
00583 set_companion_matrix (a, n - 1, m);
00584
00585 balance_companion_matrix (m, n - 1);
00586
00587 status = qr_companion (m, n - 1, z);
00588
00589 if (status)
00590 {
00591 cerr << "root solving qr method failed to converge" << endl;
00592
00593 return (GSL_FAILURE);
00594 }
00595
00596 return GSL_SUCCESS;
00597 }
00598
00599
00600