slaed4.c
来自「提供矩阵类的函数库」· C语言 代码 · 共 967 行 · 第 1/2 页
C
967 行
#include "blaswrap.h"
/* -- translated by f2c (version 19990503).
You must link the resulting object file with the libraries:
-lf2c -lm (in that order)
*/
#include "f2c.h"
/* Common Block Declarations */
struct {
real ops, itcnt;
} latime_;
#define latime_1 latime_
/* Subroutine */ int slaed4_(integer *n, integer *i__, real *d__, real *z__,
real *delta, real *rho, real *dlam, integer *info)
{
/* System generated locals */
integer i__1;
real r__1;
/* Builtin functions */
double sqrt(doublereal);
/* Local variables */
static real dphi, dpsi;
static integer iter;
static real temp, prew, temp1, a, b, c__;
static integer j;
static real w;
static integer niter;
static logical swtch;
extern /* Subroutine */ int slaed5_(integer *, real *, real *, real *,
real *, real *), slaed6_(integer *, logical *, real *, real *,
real *, real *, real *, integer *);
static logical swtch3;
static integer ii;
static real dw;
extern doublereal slamch_(char *);
static real zz[3];
static logical orgati;
static real erretm, rhoinv;
static integer ip1;
static real del, eta, phi, eps, tau, psi;
static integer iim1, iip1;
/* -- LAPACK routine (instrumented to count operations, version 3.0) --
Univ. of Tennessee, Oak Ridge National Lab, Argonne National Lab,
Courant Institute, NAG Ltd., and Rice University
June 30, 1999
Common block to return operation count and iteration count
ITCNT is unchanged, OPS is only incremented
Purpose
=======
This subroutine computes the I-th updated eigenvalue of a symmetric
rank-one modification to a diagonal matrix whose elements are
given in the array d, and that
D(i) < D(j) for i < j
and that RHO > 0. This is arranged by the calling routine, and is
no loss in generality. The rank-one modified system is thus
diag( D ) + RHO * Z * Z_transpose.
where we assume the Euclidean norm of Z is 1.
The method consists of approximating the rational functions in the
secular equation by simpler interpolating rational functions.
Arguments
=========
N (input) INTEGER
The length of all arrays.
I (input) INTEGER
The index of the eigenvalue to be computed. 1 <= I <= N.
D (input) REAL array, dimension (N)
The original eigenvalues. It is assumed that they are in
order, D(I) < D(J) for I < J.
Z (input) REAL array, dimension (N)
The components of the updating vector.
DELTA (output) REAL array, dimension (N)
If N .ne. 1, DELTA contains (D(j) - lambda_I) in its j-th
component. If N = 1, then DELTA(1) = 1. The vector DELTA
contains the information necessary to construct the
eigenvectors.
RHO (input) REAL
The scalar in the symmetric updating formula.
DLAM (output) REAL
The computed lambda_I, the I-th updated eigenvalue.
INFO (output) INTEGER
= 0: successful exit
> 0: if INFO = 1, the updating process failed.
Internal Parameters
===================
Logical variable ORGATI (origin-at-i?) is used for distinguishing
whether D(i) or D(i+1) is treated as the origin.
ORGATI = .true. origin at i
ORGATI = .false. origin at i+1
Logical variable SWTCH3 (switch-for-3-poles?) is for noting
if we are working with THREE poles!
MAXIT is the maximum number of iterations allowed for each
eigenvalue.
Further Details
===============
Based on contributions by
Ren-Cang Li, Computer Science Division, University of California
at Berkeley, USA
=====================================================================
Since this routine is called in an inner loop, we do no argument
checking.
Quick return for N=1 and 2.
Parameter adjustments */
--delta;
--z__;
--d__;
/* Function Body */
*info = 0;
if (*n == 1) {
/* Presumably, I=1 upon entry */
latime_1.ops += 3;
*dlam = d__[1] + *rho * z__[1] * z__[1];
delta[1] = 1.f;
return 0;
}
if (*n == 2) {
slaed5_(i__, &d__[1], &z__[1], &delta[1], rho, dlam);
return 0;
}
/* Compute machine epsilon */
eps = slamch_("Epsilon");
latime_1.ops += 1;
rhoinv = 1.f / *rho;
/* The case I = N */
if (*i__ == *n) {
/* Initialize some basic variables */
ii = *n - 1;
niter = 1;
/* Calculate initial guess */
latime_1.ops = latime_1.ops + *n * 5 + 1;
temp = *rho / 2.f;
/* If ||Z||_2 is not one, then TEMP should be set to
RHO * ||Z||_2^2 / TWO */
i__1 = *n;
for (j = 1; j <= i__1; ++j) {
delta[j] = d__[j] - d__[*i__] - temp;
/* L10: */
}
psi = 0.f;
i__1 = *n - 2;
for (j = 1; j <= i__1; ++j) {
psi += z__[j] * z__[j] / delta[j];
/* L20: */
}
c__ = rhoinv + psi;
w = c__ + z__[ii] * z__[ii] / delta[ii] + z__[*n] * z__[*n] / delta[*
n];
if (w <= 0.f) {
latime_1.ops += 7;
temp = z__[*n - 1] * z__[*n - 1] / (d__[*n] - d__[*n - 1] + *rho)
+ z__[*n] * z__[*n] / *rho;
if (c__ <= temp) {
tau = *rho;
} else {
latime_1.ops += 14;
del = d__[*n] - d__[*n - 1];
a = -c__ * del + z__[*n - 1] * z__[*n - 1] + z__[*n] * z__[*n]
;
b = z__[*n] * z__[*n] * del;
if (a < 0.f) {
tau = b * 2.f / (sqrt(a * a + b * 4.f * c__) - a);
} else {
tau = (a + sqrt(a * a + b * 4.f * c__)) / (c__ * 2.f);
}
}
/* It can be proved that
D(N)+RHO/2 <= LAMBDA(N) < D(N)+TAU <= D(N)+RHO */
} else {
latime_1.ops += 16;
del = d__[*n] - d__[*n - 1];
a = -c__ * del + z__[*n - 1] * z__[*n - 1] + z__[*n] * z__[*n];
b = z__[*n] * z__[*n] * del;
if (a < 0.f) {
tau = b * 2.f / (sqrt(a * a + b * 4.f * c__) - a);
} else {
tau = (a + sqrt(a * a + b * 4.f * c__)) / (c__ * 2.f);
}
/* It can be proved that
D(N) < D(N)+TAU < LAMBDA(N) < D(N)+RHO/2 */
}
latime_1.ops = latime_1.ops + (*n << 1) + ii * 6 + 14;
i__1 = *n;
for (j = 1; j <= i__1; ++j) {
delta[j] = d__[j] - d__[*i__] - tau;
/* L30: */
}
/* Evaluate PSI and the derivative DPSI */
dpsi = 0.f;
psi = 0.f;
erretm = 0.f;
i__1 = ii;
for (j = 1; j <= i__1; ++j) {
temp = z__[j] / delta[j];
psi += z__[j] * temp;
dpsi += temp * temp;
erretm += psi;
/* L40: */
}
erretm = dabs(erretm);
/* Evaluate PHI and the derivative DPHI */
temp = z__[*n] / delta[*n];
phi = z__[*n] * temp;
dphi = temp * temp;
erretm = (-phi - psi) * 8.f + erretm - phi + rhoinv + dabs(tau) * (
dpsi + dphi);
w = rhoinv + phi + psi;
/* Test for convergence */
if (dabs(w) <= eps * erretm) {
latime_1.ops += 1;
*dlam = d__[*i__] + tau;
goto L250;
}
/* Calculate the new step */
latime_1.ops += 12;
++niter;
c__ = w - delta[*n - 1] * dpsi - delta[*n] * dphi;
a = (delta[*n - 1] + delta[*n]) * w - delta[*n - 1] * delta[*n] * (
dpsi + dphi);
b = delta[*n - 1] * delta[*n] * w;
if (c__ < 0.f) {
c__ = dabs(c__);
}
if (c__ == 0.f) {
/* ETA = B/A */
latime_1.ops += 1;
eta = *rho - tau;
} else if (a >= 0.f) {
latime_1.ops += 8;
eta = (a + sqrt((r__1 = a * a - b * 4.f * c__, dabs(r__1)))) / (
c__ * 2.f);
} else {
latime_1.ops += 8;
eta = b * 2.f / (a - sqrt((r__1 = a * a - b * 4.f * c__, dabs(
r__1))));
}
/* Note, eta should be positive if w is negative, and
eta should be negative otherwise. However,
if for some reason caused by roundoff, eta*w > 0,
we simply use one Newton step instead. This way
will guarantee eta*w < 0. */
latime_1.ops = latime_1.ops + *n + ii * 6 + 16;
if (w * eta > 0.f) {
latime_1.ops += 2;
eta = -w / (dpsi + dphi);
}
temp = tau + eta;
if (temp > *rho) {
latime_1.ops += 1;
eta = *rho - tau;
}
i__1 = *n;
for (j = 1; j <= i__1; ++j) {
delta[j] -= eta;
/* L50: */
}
tau += eta;
/* Evaluate PSI and the derivative DPSI */
dpsi = 0.f;
psi = 0.f;
erretm = 0.f;
i__1 = ii;
for (j = 1; j <= i__1; ++j) {
temp = z__[j] / delta[j];
psi += z__[j] * temp;
dpsi += temp * temp;
erretm += psi;
/* L60: */
}
erretm = dabs(erretm);
/* Evaluate PHI and the derivative DPHI */
temp = z__[*n] / delta[*n];
phi = z__[*n] * temp;
dphi = temp * temp;
erretm = (-phi - psi) * 8.f + erretm - phi + rhoinv + dabs(tau) * (
dpsi + dphi);
w = rhoinv + phi + psi;
/* Main loop to update the values of the array DELTA */
iter = niter + 1;
for (niter = iter; niter <= 20; ++niter) {
/* Test for convergence */
latime_1.ops += 1;
if (dabs(w) <= eps * erretm) {
latime_1.ops += 1;
*dlam = d__[*i__] + tau;
goto L250;
}
/* Calculate the new step */
latime_1.ops = latime_1.ops + 36 + *n + ii * 6;
c__ = w - delta[*n - 1] * dpsi - delta[*n] * dphi;
a = (delta[*n - 1] + delta[*n]) * w - delta[*n - 1] * delta[*n] *
(dpsi + dphi);
b = delta[*n - 1] * delta[*n] * w;
if (a >= 0.f) {
eta = (a + sqrt((r__1 = a * a - b * 4.f * c__, dabs(r__1)))) /
(c__ * 2.f);
} else {
eta = b * 2.f / (a - sqrt((r__1 = a * a - b * 4.f * c__, dabs(
r__1))));
}
/* Note, eta should be positive if w is negative, and
eta should be negative otherwise. However,
if for some reason caused by roundoff, eta*w > 0,
we simply use one Newton step instead. This way
will guarantee eta*w < 0. */
if (w * eta > 0.f) {
eta = -w / (dpsi + dphi);
}
temp = tau + eta;
if (temp <= 0.f) {
eta /= 2.f;
}
i__1 = *n;
for (j = 1; j <= i__1; ++j) {
delta[j] -= eta;
/* L70: */
}
tau += eta;
/* Evaluate PSI and the derivative DPSI */
dpsi = 0.f;
psi = 0.f;
erretm = 0.f;
i__1 = ii;
for (j = 1; j <= i__1; ++j) {
temp = z__[j] / delta[j];
psi += z__[j] * temp;
dpsi += temp * temp;
erretm += psi;
/* L80: */
}
erretm = dabs(erretm);
/* Evaluate PHI and the derivative DPHI */
temp = z__[*n] / delta[*n];
phi = z__[*n] * temp;
dphi = temp * temp;
erretm = (-phi - psi) * 8.f + erretm - phi + rhoinv + dabs(tau) *
(dpsi + dphi);
w = rhoinv + phi + psi;
/* L90: */
}
/* Return with INFO = 1, NITER = MAXIT and not converged */
*info = 1;
latime_1.ops += 1;
*dlam = d__[*i__] + tau;
goto L250;
/* End for the case I = N */
} else {
/* The case for I < N */
niter = 1;
ip1 = *i__ + 1;
/* Calculate initial guess */
temp = (d__[ip1] - d__[*i__]) / 2.f;
i__1 = *n;
for (j = 1; j <= i__1; ++j) {
delta[j] = d__[j] - d__[*i__] - temp;
/* L100: */
}
psi = 0.f;
i__1 = *i__ - 1;
for (j = 1; j <= i__1; ++j) {
psi += z__[j] * z__[j] / delta[j];
/* L110: */
}
phi = 0.f;
i__1 = *i__ + 2;
for (j = *n; j >= i__1; --j) {
phi += z__[j] * z__[j] / delta[j];
/* L120: */
}
c__ = rhoinv + psi + phi;
w = c__ + z__[*i__] * z__[*i__] / delta[*i__] + z__[ip1] * z__[ip1] /
delta[ip1];
if (w > 0.f) {
/* d(i)< the ith eigenvalue < (d(i)+d(i+1))/2
We choose d(i) as origin. */
orgati = TRUE_;
del = d__[ip1] - d__[*i__];
a = c__ * del + z__[*i__] * z__[*i__] + z__[ip1] * z__[ip1];
b = z__[*i__] * z__[*i__] * del;
if (a > 0.f) {
tau = b * 2.f / (a + sqrt((r__1 = a * a - b * 4.f * c__, dabs(
r__1))));
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?