📄 francis.c
字号:
*/ dat[0] = (h_tmp1*h_tmp2 - h_cross) / gsl_matrix_get(H, 1, 0) + gsl_matrix_get(H, 0, 1); dat[1] = gsl_matrix_get(H, 1, 1) - gsl_matrix_get(H, 0, 0) - h_tmp1 - h_tmp2; dat[2] = gsl_matrix_get(H, 2, 1); scale = fabs(dat[0]) + fabs(dat[1]) + fabs(dat[2]); if (scale != 0.0) { /* scale to prevent overflow or underflow */ dat[0] /= scale; dat[1] /= scale; dat[2] /= scale; } if (w->Z || w->compute_t) { /* * get absolute indices of this (sub)matrix relative to the * original Hessenberg matrix */ top = francis_get_submatrix(w->H, H); } for (i = 0; i < N - 2; ++i) { tau_i = gsl_linalg_householder_transform(&v3.vector); if (tau_i != 0.0) { /* q = max(1, i - 1) */ q = (1 > ((int)i - 1)) ? 0 : (i - 1); /* r = min(i + 3, N - 1) */ r = ((i + 3) < (N - 1)) ? (i + 3) : (N - 1); if (w->compute_t) { /* * We are computing the Schur form T, so we * need to transform the whole matrix H * * H -> P_k^t H P_k * * where P_k is the current Householder matrix */ /* apply left householder matrix (I - tau_i v v') to H */ m = gsl_matrix_submatrix(w->H, top + i, top + q, 3, w->size - top - q); gsl_linalg_householder_hm(tau_i, &v3.vector, &m.matrix); /* apply right householder matrix (I - tau_i v v') to H */ m = gsl_matrix_submatrix(w->H, 0, top + i, top + r + 1, 3); gsl_linalg_householder_mh(tau_i, &v3.vector, &m.matrix); } else { /* * We are not computing the Schur form T, so we * only need to transform the active block */ /* apply left householder matrix (I - tau_i v v') to H */ m = gsl_matrix_submatrix(H, i, q, 3, N - q); gsl_linalg_householder_hm(tau_i, &v3.vector, &m.matrix); /* apply right householder matrix (I - tau_i v v') to H */ m = gsl_matrix_submatrix(H, 0, i, r + 1, 3); gsl_linalg_householder_mh(tau_i, &v3.vector, &m.matrix); } if (w->Z) { /* accumulate the similarity transformation into Z */ m = gsl_matrix_submatrix(w->Z, 0, top + i, w->size, 3); gsl_linalg_householder_mh(tau_i, &v3.vector, &m.matrix); } } /* if (tau_i != 0.0) */ dat[0] = gsl_matrix_get(H, i + 1, i); dat[1] = gsl_matrix_get(H, i + 2, i); if (i < (N - 3)) { dat[2] = gsl_matrix_get(H, i + 3, i); } scale = fabs(dat[0]) + fabs(dat[1]) + fabs(dat[2]); if (scale != 0.0) { /* scale to prevent overflow or underflow */ dat[0] /= scale; dat[1] /= scale; dat[2] /= scale; } } /* for (i = 0; i < N - 2; ++i) */ scale = fabs(dat[0]) + fabs(dat[1]); if (scale != 0.0) { /* scale to prevent overflow or underflow */ dat[0] /= scale; dat[1] /= scale; } tau_i = gsl_linalg_householder_transform(&v2.vector); if (w->compute_t) { m = gsl_matrix_submatrix(w->H, top + N - 2, top + N - 3, 2, w->size - top - N + 3); gsl_linalg_householder_hm(tau_i, &v2.vector, &m.matrix); m = gsl_matrix_submatrix(w->H, 0, top + N - 2, top + N, 2); gsl_linalg_householder_mh(tau_i, &v2.vector, &m.matrix); } else { m = gsl_matrix_submatrix(H, N - 2, N - 3, 2, 3); gsl_linalg_householder_hm(tau_i, &v2.vector, &m.matrix); m = gsl_matrix_submatrix(H, 0, N - 2, N, 2); gsl_linalg_householder_mh(tau_i, &v2.vector, &m.matrix); } if (w->Z) { /* accumulate transformation into Z */ m = gsl_matrix_submatrix(w->Z, 0, top + N - 2, w->size, 2); gsl_linalg_householder_mh(tau_i, &v2.vector, &m.matrix); } return GSL_SUCCESS;} /* francis_qrstep() *//*francis_search_subdiag_small_elements() Search for a small subdiagonal element starting from the bottomof a matrix A. A small element is one that satisfies:|A_{i,i-1}| <= eps * (|A_{i,i}| + |A_{i-1,i-1}|)Inputs: A - matrix (must be at least 3-by-3)Return: row index of small subdiagonal element or 0 if not foundNotes: the first small element that is found (starting from bottom) is set to zero*/static inline size_tfrancis_search_subdiag_small_elements(gsl_matrix * A){ const size_t N = A->size1; size_t i; double dpel = gsl_matrix_get(A, N - 2, N - 2); for (i = N - 1; i > 0; --i) { double sel = gsl_matrix_get(A, i, i - 1); double del = gsl_matrix_get(A, i, i); if ((sel == 0.0) || (fabs(sel) < GSL_DBL_EPSILON * (fabs(del) + fabs(dpel)))) { gsl_matrix_set(A, i, i - 1, 0.0); return (i); } dpel = del; } return (0);} /* francis_search_subdiag_small_elements() *//*francis_schur_standardize() Convert a 2-by-2 diagonal block in the Schur form to standard formand update the rest of T and Z matrices if required.Inputs: A - 2-by-2 matrix eval1 - where to store eigenvalue 1 eval2 - where to store eigenvalue 2 w - francis workspace*/static inline voidfrancis_schur_standardize(gsl_matrix *A, gsl_complex *eval1, gsl_complex *eval2, gsl_eigen_francis_workspace *w){ const size_t N = w->size; double cs, sn; size_t top; /* * figure out where the submatrix A resides in the * original matrix H */ top = francis_get_submatrix(w->H, A); /* convert 2-by-2 block to standard form */ francis_standard_form(A, &cs, &sn); /* set eigenvalues */ GSL_SET_REAL(eval1, gsl_matrix_get(A, 0, 0)); GSL_SET_REAL(eval2, gsl_matrix_get(A, 1, 1)); if (gsl_matrix_get(A, 1, 0) == 0.0) { GSL_SET_IMAG(eval1, 0.0); GSL_SET_IMAG(eval2, 0.0); } else { double tmp = sqrt(fabs(gsl_matrix_get(A, 0, 1)) * fabs(gsl_matrix_get(A, 1, 0))); GSL_SET_IMAG(eval1, tmp); GSL_SET_IMAG(eval2, -tmp); } if (w->compute_t) { gsl_vector_view xv, yv; /* * The above call to francis_standard_form transformed a 2-by-2 block * of T into upper triangular form via the transformation * * U = [ CS -SN ] * [ SN CS ] * * The original matrix T was * * T = [ T_{11} | T_{12} | T_{13} ] * [ 0* | A | T_{23} ] * [ 0 | 0* | T_{33} ] * * where 0* indicates all zeros except for possibly * one subdiagonal element next to A. * * After francis_standard_form, T looks like this: * * T = [ T_{11} | T_{12} | T_{13} ] * [ 0* | U^t A U | T_{23} ] * [ 0 | 0* | T_{33} ] * * since only the 2-by-2 block of A was changed. However, * in order to be able to back transform T at the end, * we need to apply the U transformation to the rest * of the matrix T since there is no way to apply a * similarity transformation to T and change only the * middle 2-by-2 block. In other words, let * * M = [ I 0 0 ] * [ 0 U 0 ] * [ 0 0 I ] * * and compute * * M^t T M = [ T_{11} | T_{12} U | T_{13} ] * [ U^t 0* | U^t A U | U^t T_{23} ] * [ 0 | 0* U | T_{33} ] * * So basically we need to apply the transformation U * to the i x 2 matrix T_{12} and the 2 x (n - i + 2) * matrix T_{23}, where i is the index of the top of A * in T. * * The BLAS routine drot() is suited for this. */ if (top < (N - 2)) { /* transform the 2 rows of T_{23} */ xv = gsl_matrix_subrow(w->H, top, top + 2, N - top - 2); yv = gsl_matrix_subrow(w->H, top + 1, top + 2, N - top - 2); gsl_blas_drot(&xv.vector, &yv.vector, cs, sn); } if (top > 0) { /* transform the 2 columns of T_{12} */ xv = gsl_matrix_subcolumn(w->H, top, 0, top); yv = gsl_matrix_subcolumn(w->H, top + 1, 0, top); gsl_blas_drot(&xv.vector, &yv.vector, cs, sn); } } /* if (w->compute_t) */ if (w->Z) { gsl_vector_view xv, yv; /* * Accumulate the transformation in Z. Here, Z -> Z * M * * So: * * Z -> [ Z_{11} | Z_{12} U | Z_{13} ] * [ Z_{21} | Z_{22} U | Z_{23} ] * [ Z_{31} | Z_{32} U | Z_{33} ] * * So we just need to apply drot() to the 2 columns * starting at index 'top' */ xv = gsl_matrix_column(w->Z, top); yv = gsl_matrix_column(w->Z, top + 1); gsl_blas_drot(&xv.vector, &yv.vector, cs, sn); } /* if (w->Z) */} /* francis_schur_standardize() *//*francis_get_submatrix() B is a submatrix of A. The goal of this function is tocompute the indices in A of where the matrix B resides*/static inline size_tfrancis_get_submatrix(gsl_matrix *A, gsl_matrix *B){ size_t diff; double ratio; size_t top; diff = (size_t) (B->data - A->data); ratio = (double)diff / ((double) (A->tda + 1)); top = (size_t) floor(ratio); return top;} /* francis_get_submatrix() *//*francis_standard_form() Compute the Schur factorization of a real 2-by-2 matrix instandard form:[ A B ] = [ CS -SN ] [ T11 T12 ] [ CS SN ][ C D ] [ SN CS ] [ T21 T22 ] [-SN CS ]where either:1) T21 = 0 so that T11 and T22 are real eigenvalues of the matrix, or2) T11 = T22 and T21*T12 < 0, so that T11 +/- sqrt(|T21*T12|) are complex conjugate eigenvaluesInputs: A - 2-by-2 matrix cs - where to store cosine parameter of rotation matrix sn - where to store sine parameter of rotation matrixNotes: 1) based on LAPACK routine DLANV2 2) On output, A is modified to contain the matrix in standard form*/static voidfrancis_standard_form(gsl_matrix *A, double *cs, double *sn){ double a, b, c, d; /* input matrix values */ double tmp; double p, z; double bcmax, bcmis, scale; double tau, sigma; double cs1, sn1; double aa, bb, cc, dd; double sab, sac; a = gsl_matrix_get(A, 0, 0); b = gsl_matrix_get(A, 0, 1); c = gsl_matrix_get(A, 1, 0); d = gsl_matrix_get(A, 1, 1); if (c == 0.0) { /* * matrix is already upper triangular - set rotation matrix * to the identity */ *cs = 1.0; *sn = 0.0; } else if (b == 0.0) { /* swap rows and columns to make it upper triangular */ *cs = 0.0; *sn = 1.0; tmp = d; d = a; a = tmp; b = -c; c = 0.0; } else if (((a - d) == 0.0) && (GSL_SIGN(b) != GSL_SIGN(c))) { /* the matrix has complex eigenvalues with a == d */ *cs = 1.0; *sn = 0.0; } else { tmp = a - d; p = 0.5 * tmp; bcmax = GSL_MAX(fabs(b), fabs(c)); bcmis = GSL_MIN(fabs(b), fabs(c)) * GSL_SIGN(b) * GSL_SIGN(c); scale = GSL_MAX(fabs(p), bcmax); z = (p / scale) * p + (bcmax / scale) * bcmis; if (z >= 4.0 * GSL_DBL_EPSILON) { /* real eigenvalues, compute a and d */ z = p + GSL_SIGN(p) * fabs(sqrt(scale) * sqrt(z)); a = d + z; d -= (bcmax / z) * bcmis; /* compute b and the rotation matrix */ tau = gsl_hypot(c, z); *cs = z / tau; *sn = c / tau; b -= c; c = 0.0; } else { /* * complex eigenvalues, or real (almost) equal eigenvalues - * make diagonal elements equal */ sigma = b + c; tau = gsl_hypot(sigma, tmp); *cs = sqrt(0.5 * (1.0 + fabs(sigma) / tau)); *sn = -(p / (tau * (*cs))) * GSL_SIGN(sigma); /* * Compute [ AA BB ] = [ A B ] [ CS -SN ] * [ CC DD ] [ C D ] [ SN CS ] */ aa = a * (*cs) + b * (*sn); bb = -a * (*sn) + b * (*cs); cc = c * (*cs) + d * (*sn); dd = -c * (*sn) + d * (*cs); /* * Compute [ A B ] = [ CS SN ] [ AA BB ] * [ C D ] [-SN CS ] [ CC DD ] */ a = aa * (*cs) + cc * (*sn); b = bb * (*cs) + dd * (*sn); c = -aa * (*sn) + cc * (*cs); d = -bb * (*sn) + dd * (*cs); tmp = 0.5 * (a + d); a = d = tmp; if (c != 0.0) { if (b != 0.0) { if (GSL_SIGN(b) == GSL_SIGN(c)) { /* * real eigenvalues: reduce to upper triangular * form */ sab = sqrt(fabs(b)); sac = sqrt(fabs(c)); p = GSL_SIGN(c) * fabs(sab * sac); tau = 1.0 / sqrt(fabs(b + c)); a = tmp + p; d = tmp - p; b -= c; c = 0.0; cs1 = sab * tau; sn1 = sac * tau; tmp = (*cs) * cs1 - (*sn) * sn1; *sn = (*cs) * sn1 + (*sn) * cs1; *cs = tmp; } } else { b = -c; c = 0.0; tmp = *cs; *cs = -(*sn); *sn = tmp; } } } } /* set new matrix elements */ gsl_matrix_set(A, 0, 0, a); gsl_matrix_set(A, 0, 1, b); gsl_matrix_set(A, 1, 0, c); gsl_matrix_set(A, 1, 1, d);} /* francis_standard_form() */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -