#ifndef TNT_LINALG_H_ #define TNT_LINALG_H_ #include // for min(), max() below #include // for abs(), sqrt() below namespace TNT { namespace Linear_Algebra { using namespace std; /**

For a symmetric, positive definite matrix A, this function computes the Cholesky factorization, i.e. it computes a lower triangular matrix L such that A = L*L'. If the matrix is not symmetric or positive definite, the function computes only a partial decomposition. This can be tested with the is_spd() flag.

Typical usage looks like:

	Matrix A(n,n);
	Matrix L;

	 ... 

	Cholesky chol(A);

	if (chol.is_spd())
		L = chol.getL();
		
  	else
		cout << "factorization was not complete.\n";

	

(Adapted from JAMA, a Java Matrix Library, developed by jointly by the Mathworks and NIST; see http://math.nist.gov/javanumerics/jama). */ template class Cholesky { Matrix L_; // lower triangular factor int isspd; // 1 if matrix to be factored was SPD public: Cholesky(); Cholesky(const Matrix &A); Matrix getL() const; Vector solve(const Vector &B); Matrix solve(const Matrix &B); int is_spd() const; }; template Cholesky::Cholesky() : L_(Real(0.0)), isspd(0) {} /** @return 1, if original matrix to be factored was symmetric positive-definite (SPD). */ template int Cholesky::is_spd() const { return isspd; } /** @return the lower triangular factor, L, such that L*L'=A. */ template Matrix Cholesky::getL() const { return L_; } /** Constructs a lower triangular matrix L, such that L*L'= A. If A is not symmetric positive-definite (SPD), only a partial factorization is performed. If is_spd() evalutate true (1) then the factorizaiton was successful. */ template Cholesky::Cholesky(const Matrix &A) { int m = A.num_rows(); int n = A.num_cols(); isspd = (m == n); if (m != n) { L_ = Matrix(Real(0.0)); return; } L_ = Matrix(n,n); // Main loop. for (int j = 0; j < n; j++) { Real d = Real(0.0); for (int k = 0; k < j; k++) { Real s = Real(0.0); for (int i = 0; i < k; i++) { s += L_[k][i]*L_[j][i]; } L_[j][k] = s = (A[j][k] - s)/L_[k][k]; d = d + s*s; isspd = isspd && (A[k][j] == A[j][k]); } d = A[j][j] - d; isspd = isspd && (d > Real(0.0)); L_[j][j] = sqrt(d > Real(0.0) ? d : Real(0.0)); for (int k = j+1; k < n; k++) { L_[j][k] = Real(0.0); } } } /** Solve a linear system A*x = b, using the previously computed cholesky factorization of A: L*L'. @param b matrix with as many rows as A and any number of columns. @return x so that L*L'*x = b. If b is nonconformat, or if A was not symmetric posidtive definite, a null (Real(0.0)) array is returned. */ template Vector Cholesky::solve(const Vector &b) { int n = L_.num_rows(); if (b.dim() != n) return Vector(); Vector x = b; // Solve L*y = b; for (int k = 0; k < n; k++) { for (int i = 0; i < k; i++) x[k] -= x[i]*L_[k][i]; x[k] /= L_[k][k]; } // Solve L'*X = Y; for (int k = n-1; k >= 0; k--) { for (int i = k+1; i < n; i++) x[k] -= x[i]*L_[i][k]; x[k] /= L_[k][k]; } return x; } /** Solve a linear system A*X = B, using the previously computed cholesky factorization of A: L*L'. @param B A Matrix with as many rows as A and any number of columns. @return X so that L*L'*X = B. If B is nonconformat, or if A was not symmetric posidtive definite, a null (Real(0.0)) array is returned. */ template Matrix Cholesky::solve(const Matrix &B) { int n = L_.num_rows(); if (B.num_rows() != n) return Matrix(); Matrix X = B; int nx = B.num_cols(); // Solve L*y = b; for (int j=0; j< nx; j++) { for (int k = 0; k < n; k++) { for (int i = 0; i < k; i++) X[k][j] -= X[i][j]*L_[k][i]; X[k][j] /= L_[k][k]; } } // Solve L'*X = Y; for (int j=0; j= 0; k--) { for (int i = k+1; i < n; i++) X[k][j] -= X[i][j]*L_[i][k]; X[k][j] /= L_[k][k]; } } return X; } /** Computes eigenvalues and eigenvectors of a real (non-complex) matrix.

If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is diagonal and the eigenvector matrix V is orthogonal. That is, the diagonal values of D are the eigenvalues, and V*V' = I, where I is the identity matrix. The columns of V represent the eigenvectors in the sense that A*V = V*D.

If A is not symmetric, then the eigenvalue matrix D is block diagonal with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues, a + i*b, in 2-by-2 blocks, [a, b; -b, a]. That is, if the complex eigenvalues look like


          u + iv     .        .          .      .    .
            .      u - iv     .          .      .    .
            .        .      a + ib       .      .    .
            .        .        .        a - ib   .    .
            .        .        .          .      x    .
            .        .        .          .      .    y
then D looks like

            u        v        .          .      .    .
           -v        u        .          .      .    . 
            .        .        a          b      .    .
            .        .       -b          a      .    .
            .        .        .          .      x    .
            .        .        .          .      .    y
This keeps V a real matrix in both symmetric and non-symmetric cases, and A*V = V*D.

The matrix V may be badly conditioned, or even singular, so the validity of the equation A = V*D*inverse(V) depends upon the condition number of V.

(Adapted from JAMA, a Java Matrix Library, developed by jointly by the Mathworks and NIST (see http://math.nist.gov/javanumerics/jama), which in turn, were based on original EISPACK routines. **/ template class Eigenvalue { /** Row and column dimension (square matrix). */ int n; int issymmetric; /* boolean*/ /** Arrays for internal storage of eigenvalues. */ Vector d; /* real part */ Vector e; /* img part */ /** Array for internal storage of eigenvectors. */ Matrix V; /* Array for internal storage of nonsymmetric Hessenberg form. @serial internal storage of nonsymmetric Hessenberg form. */ Matrix H; /* Working storage for nonsymmetric algorithm. @serial working storage for nonsymmetric algorithm. */ Vector ort; // Symmetric Householder reduction to tridiagonal form. void tred2() { // This is derived from the Algol procedures tred2 by // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding // Fortran subroutine in EISPACK. for (int j = 0; j < n; j++) { d[j] = V[n-1][j]; } // Householder reduction to tridiagonal form. for (int i = n-1; i > 0; i--) { // Scale to avoid under/overflow. Real scale = Real(0.0); Real h = Real(0.0); for (int k = 0; k < i; k++) { scale = scale + abs(d[k]); } if (scale == Real(0.0)) { e[i] = d[i-1]; for (int j = 0; j < i; j++) { d[j] = V[i-1][j]; V[i][j] = Real(0.0); V[j][i] = Real(0.0); } } else { // Generate Householder vector. for (int k = 0; k < i; k++) { d[k] /= scale; h += d[k] * d[k]; } Real f = d[i-1]; Real g = sqrt(h); if (f > 0) { g = -g; } e[i] = scale * g; h = h - f * g; d[i-1] = f - g; for (int j = 0; j < i; j++) { e[j] = Real(0.0); } // Apply similarity transformation to remaining columns. for (int j = 0; j < i; j++) { f = d[j]; V[j][i] = f; g = e[j] + V[j][j] * f; for (int k = j+1; k <= i-1; k++) { g += V[k][j] * d[k]; e[k] += V[k][j] * f; } e[j] = g; } f = Real(0.0); for (int j = 0; j < i; j++) { e[j] /= h; f += e[j] * d[j]; } Real hh = f / (h + h); for (int j = 0; j < i; j++) { e[j] -= hh * d[j]; } for (int j = 0; j < i; j++) { f = d[j]; g = e[j]; for (int k = j; k <= i-1; k++) { V[k][j] -= (f * e[k] + g * d[k]); } d[j] = V[i-1][j]; V[i][j] = Real(0.0); } } d[i] = h; } // Accumulate transformations. for (int i = 0; i < n-1; i++) { V[n-1][i] = V[i][i]; V[i][i] = Real(1.0); Real h = d[i+1]; if (h != Real(0.0)) { for (int k = 0; k <= i; k++) { d[k] = V[k][i+1] / h; } for (int j = 0; j <= i; j++) { Real g = Real(0.0); for (int k = 0; k <= i; k++) { g += V[k][i+1] * V[k][j]; } for (int k = 0; k <= i; k++) { V[k][j] -= g * d[k]; } } } for (int k = 0; k <= i; k++) { V[k][i+1] = Real(0.0); } } for (int j = 0; j < n; j++) { d[j] = V[n-1][j]; V[n-1][j] = Real(0.0); } V[n-1][n-1] = Real(1.0); e[0] = Real(0.0); } // Symmetric tridiagonal QL algorithm. void tql2 () { // This is derived from the Algol procedures tql2, by // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding // Fortran subroutine in EISPACK. for (int i = 1; i < n; i++) { e[i-1] = e[i]; } e[n-1] = Real(0.0); Real f = Real(0.0); Real tst1 = Real(0.0); Real eps = pow(2.0,-52.0); for (int l = 0; l < n; l++) { // Find small subdiagonal element tst1 = max(tst1,abs(d[l]) + abs(e[l])); int m = l; // Original while-loop from Java code while (m < n) { if (abs(e[m]) <= eps*tst1) { break; } m++; } // If m == l, d[l] is an eigenvalue, // otherwise, iterate. if (m > l) { int iter = 0; do { iter = iter + 1; // (Could check iteration count here.) // Compute implicit shift Real g = d[l]; Real p = (d[l+1] - g) / (2.0 * e[l]); Real r = hypot(p, static_cast(Real(1.0))); if (p < 0) { r = -r; } d[l] = e[l] / (p + r); d[l+1] = e[l] * (p + r); Real dl1 = d[l+1]; Real h = g - d[l]; for (int i = l+2; i < n; i++) { d[i] -= h; } f = f + h; // Implicit QL transformation. p = d[m]; Real c = Real(1.0); Real c2 = c; Real c3 = c; Real el1 = e[l+1]; Real s = Real(0.0); Real s2 = Real(0.0); for (int i = m-1; i >= l; i--) { c3 = c2; c2 = c; s2 = s; g = c * e[i]; h = c * p; r = hypot(p,e[i]); e[i+1] = s * r; s = e[i] / r; c = p / r; p = c * d[i] - s * g; d[i+1] = h + s * (c * g + s * d[i]); // Accumulate transformation. for (int k = 0; k < n; k++) { h = V[k][i+1]; V[k][i+1] = s * V[k][i] + c * h; V[k][i] = c * V[k][i] - s * h; } } p = -s * s2 * c3 * el1 * e[l] / dl1; e[l] = s * p; d[l] = c * p; // Check for convergence. } while (abs(e[l]) > eps*tst1); } d[l] = d[l] + f; e[l] = Real(0.0); } // Sort eigenvalues and corresponding vectors. for (int i = 0; i < n-1; i++) { int k = i; Real p = d[i]; for (int j = i+1; j < n; j++) { if (d[j] < p) { k = j; p = d[j]; } } if (k != i) { d[k] = d[i]; d[i] = p; for (int j = 0; j < n; j++) { p = V[j][i]; V[j][i] = V[j][k]; V[j][k] = p; } } } } // Nonsymmetric reduction to Hessenberg form. void orthes () { // This is derived from the Algol procedures orthes and ortran, // by Martin and Wilkinson, Handbook for Auto. Comp., // Vol.ii-Linear Algebra, and the corresponding // Fortran subroutines in EISPACK. int low = 0; int high = n-1; for (int m = low+1; m <= high-1; m++) { // Scale column. Real scale = Real(0.0); for (int i = m; i <= high; i++) { scale = scale + abs(H[i][m-1]); } if (scale != Real(0.0)) { // Compute Householder transformation. Real h = Real(0.0); for (int i = high; i >= m; i--) { ort[i] = H[i][m-1]/scale; h += ort[i] * ort[i]; } Real g = sqrt(h); if (ort[m] > 0) { g = -g; } h = h - ort[m] * g; ort[m] = ort[m] - g; // Apply Householder similarity transformation // H = (I-u*u'/h)*H*(I-u*u')/h) for (int j = m; j < n; j++) { Real f = Real(0.0); for (int i = high; i >= m; i--) { f += ort[i]*H[i][j]; } f = f/h; for (int i = m; i <= high; i++) { H[i][j] -= f*ort[i]; } } for (int i = 0; i <= high; i++) { Real f = Real(0.0); for (int j = high; j >= m; j--) { f += ort[j]*H[i][j]; } f = f/h; for (int j = m; j <= high; j++) { H[i][j] -= f*ort[j]; } } ort[m] = scale*ort[m]; H[m][m-1] = scale*g; } } // Accumulate transformations (Algol's ortran). for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { V[i][j] = (i == j ? Real(1.0) : Real(0.0)); } } for (int m = high-1; m >= low+1; m--) { if (H[m][m-1] != Real(0.0)) { for (int i = m+1; i <= high; i++) { ort[i] = H[i][m-1]; } for (int j = m; j <= high; j++) { Real g = Real(0.0); for (int i = m; i <= high; i++) { g += ort[i] * V[i][j]; } // Double division avoids possible underflow g = (g / ort[m]) / H[m][m-1]; for (int i = m; i <= high; i++) { V[i][j] += g * ort[i]; } } } } } // Complex scalar division. Real cdivr, cdivi; void cdiv(Real xr, Real xi, Real yr, Real yi) { Real r,d; if (abs(yr) > abs(yi)) { r = yi/yr; d = yr + r*yi; cdivr = (xr + r*xi)/d; cdivi = (xi - r*xr)/d; } else { r = yr/yi; d = yi + r*yr; cdivr = (r*xr + xi)/d; cdivi = (r*xi - xr)/d; } } // Nonsymmetric reduction from Hessenberg to real Schur form. void hqr2 () { // This is derived from the Algol procedure hqr2, // by Martin and Wilkinson, Handbook for Auto. Comp., // Vol.ii-Linear Algebra, and the corresponding // Fortran subroutine in EISPACK. // Initialize int nn = this->n; int n = nn-1; int low = 0; int high = nn-1; Real eps = pow(2.0,-52.0); Real exshift = Real(0.0); Real p=0,q=0,r=0,s=0,z=0,t,w,x,y; // Store roots isolated by balanc and compute matrix norm Real norm = Real(0.0); for (int i = 0; i < nn; i++) { if ((i < low) || (i > high)) { d[i] = H[i][i]; e[i] = Real(0.0); } for (int j = max(i-1,0); j < nn; j++) { norm = norm + abs(H[i][j]); } } // Outer loop over eigenvalue index int iter = 0; while (n >= low) { // Look for single small sub-diagonal element int l = n; while (l > low) { s = abs(H[l-1][l-1]) + abs(H[l][l]); if (s == Real(0.0)) { s = norm; } if (abs(H[l][l-1]) < eps * s) { break; } l--; } // Check for convergence // One root found if (l == n) { H[n][n] = H[n][n] + exshift; d[n] = H[n][n]; e[n] = Real(0.0); n--; iter = 0; // Two roots found } else if (l == n-1) { w = H[n][n-1] * H[n-1][n]; p = (H[n-1][n-1] - H[n][n]) / 2.0; q = p * p + w; z = sqrt(abs(q)); H[n][n] = H[n][n] + exshift; H[n-1][n-1] = H[n-1][n-1] + exshift; x = H[n][n]; // Real pair if (q >= 0) { if (p >= 0) { z = p + z; } else { z = p - z; } d[n-1] = x + z; d[n] = d[n-1]; if (z != Real(0.0)) { d[n] = x - w / z; } e[n-1] = Real(0.0); e[n] = Real(0.0); x = H[n][n-1]; s = abs(x) + abs(z); p = x / s; q = z / s; r = sqrt(p * p+q * q); p = p / r; q = q / r; // Row modification for (int j = n-1; j < nn; j++) { z = H[n-1][j]; H[n-1][j] = q * z + p * H[n][j]; H[n][j] = q * H[n][j] - p * z; } // Column modification for (int i = 0; i <= n; i++) { z = H[i][n-1]; H[i][n-1] = q * z + p * H[i][n]; H[i][n] = q * H[i][n] - p * z; } // Accumulate transformations for (int i = low; i <= high; i++) { z = V[i][n-1]; V[i][n-1] = q * z + p * V[i][n]; V[i][n] = q * V[i][n] - p * z; } // Complex pair } else { d[n-1] = x + p; d[n] = x + p; e[n-1] = z; e[n] = -z; } n = n - 2; iter = 0; // No convergence yet } else { // Form shift x = H[n][n]; y = Real(0.0); w = Real(0.0); if (l < n) { y = H[n-1][n-1]; w = H[n][n-1] * H[n-1][n]; } // Wilkinson's original ad hoc shift if (iter == 10) { exshift += x; for (int i = low; i <= n; i++) { H[i][i] -= x; } s = abs(H[n][n-1]) + abs(H[n-1][n-2]); x = y = 0.75 * s; w = -0.4375 * s * s; } // MATLAB's new ad hoc shift if (iter == 30) { s = (y - x) / 2.0; s = s * s + w; if (s > 0) { s = sqrt(s); if (y < x) { s = -s; } s = x - w / ((y - x) / 2.0 + s); for (int i = low; i <= n; i++) { H[i][i] -= s; } exshift += s; x = y = w = 0.964; } } iter = iter + 1; // (Could check iteration count here.) // Look for two consecutive small sub-diagonal elements int m = n-2; while (m >= l) { z = H[m][m]; r = x - z; s = y - z; p = (r * s - w) / H[m+1][m] + H[m][m+1]; q = H[m+1][m+1] - z - r - s; r = H[m+2][m+1]; s = abs(p) + abs(q) + abs(r); p = p / s; q = q / s; r = r / s; if (m == l) { break; } if (abs(H[m][m-1]) * (abs(q) + abs(r)) < eps * (abs(p) * (abs(H[m-1][m-1]) + abs(z) + abs(H[m+1][m+1])))) { break; } m--; } for (int i = m+2; i <= n; i++) { H[i][i-2] = Real(0.0); if (i > m+2) { H[i][i-3] = Real(0.0); } } // Double QR step involving rows l:n and columns m:n for (int k = m; k <= n-1; k++) { int notlast = (k != n-1); if (k != m) { p = H[k][k-1]; q = H[k+1][k-1]; r = (notlast ? H[k+2][k-1] : Real(0.0)); x = abs(p) + abs(q) + abs(r); if (x != Real(0.0)) { p = p / x; q = q / x; r = r / x; } } if (x == Real(0.0)) { break; } s = sqrt(p * p + q * q + r * r); if (p < 0) { s = -s; } if (s != 0) { if (k != m) { H[k][k-1] = -s * x; } else if (l != m) { H[k][k-1] = -H[k][k-1]; } p = p + s; x = p / s; y = q / s; z = r / s; q = q / p; r = r / p; // Row modification for (int j = k; j < nn; j++) { p = H[k][j] + q * H[k+1][j]; if (notlast) { p = p + r * H[k+2][j]; H[k+2][j] = H[k+2][j] - p * z; } H[k][j] = H[k][j] - p * x; H[k+1][j] = H[k+1][j] - p * y; } // Column modification for (int i = 0; i <= min(n,k+3); i++) { p = x * H[i][k] + y * H[i][k+1]; if (notlast) { p = p + z * H[i][k+2]; H[i][k+2] = H[i][k+2] - p * r; } H[i][k] = H[i][k] - p; H[i][k+1] = H[i][k+1] - p * q; } // Accumulate transformations for (int i = low; i <= high; i++) { p = x * V[i][k] + y * V[i][k+1]; if (notlast) { p = p + z * V[i][k+2]; V[i][k+2] = V[i][k+2] - p * r; } V[i][k] = V[i][k] - p; V[i][k+1] = V[i][k+1] - p * q; } } // (s != 0) } // k loop } // check convergence } // while (n >= low) // Backsubstitute to find vectors of upper triangular form if (norm == Real(0.0)) { return; } for (n = nn-1; n >= 0; n--) { p = d[n]; q = e[n]; // Real vector if (q == 0) { int l = n; H[n][n] = Real(1.0); for (int i = n-1; i >= 0; i--) { w = H[i][i] - p; r = Real(0.0); for (int j = l; j <= n; j++) { r = r + H[i][j] * H[j][n]; } if (e[i] < Real(0.0)) { z = w; s = r; } else { l = i; if (e[i] == Real(0.0)) { if (w != Real(0.0)) { H[i][n] = -r / w; } else { H[i][n] = -r / (eps * norm); } // Solve real equations } else { x = H[i][i+1]; y = H[i+1][i]; q = (d[i] - p) * (d[i] - p) + e[i] * e[i]; t = (x * s - z * r) / q; H[i][n] = t; if (abs(x) > abs(z)) { H[i+1][n] = (-r - w * t) / x; } else { H[i+1][n] = (-s - y * t) / z; } } // Overflow control t = abs(H[i][n]); if ((eps * t) * t > 1) { for (int j = i; j <= n; j++) { H[j][n] = H[j][n] / t; } } } } // Complex vector } else if (q < 0) { int l = n-1; // Last vector component imaginary so matrix is triangular if (abs(H[n][n-1]) > abs(H[n-1][n])) { H[n-1][n-1] = q / H[n][n-1]; H[n-1][n] = -(H[n][n] - p) / H[n][n-1]; } else { cdiv(Real(0.0),-H[n-1][n],H[n-1][n-1]-p,q); H[n-1][n-1] = cdivr; H[n-1][n] = cdivi; } H[n][n-1] = Real(0.0); H[n][n] = Real(1.0); for (int i = n-2; i >= 0; i--) { Real ra,sa,vr,vi; ra = Real(0.0); sa = Real(0.0); for (int j = l; j <= n; j++) { ra = ra + H[i][j] * H[j][n-1]; sa = sa + H[i][j] * H[j][n]; } w = H[i][i] - p; if (e[i] < Real(0.0)) { z = w; r = ra; s = sa; } else { l = i; if (e[i] == 0) { cdiv(-ra,-sa,w,q); H[i][n-1] = cdivr; H[i][n] = cdivi; } else { // Solve complex equations x = H[i][i+1]; y = H[i+1][i]; vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q; vi = (d[i] - p) * 2.0 * q; if ((vr == Real(0.0)) && (vi == Real(0.0))) { vr = eps * norm * (abs(w) + abs(q) + abs(x) + abs(y) + abs(z)); } cdiv(x*r-z*ra+q*sa,x*s-z*sa-q*ra,vr,vi); H[i][n-1] = cdivr; H[i][n] = cdivi; if (abs(x) > (abs(z) + abs(q))) { H[i+1][n-1] = (-ra - w * H[i][n-1] + q * H[i][n]) / x; H[i+1][n] = (-sa - w * H[i][n] - q * H[i][n-1]) / x; } else { cdiv(-r-y*H[i][n-1],-s-y*H[i][n],z,q); H[i+1][n-1] = cdivr; H[i+1][n] = cdivi; } } // Overflow control t = max(abs(H[i][n-1]),abs(H[i][n])); if ((eps * t) * t > 1) { for (int j = i; j <= n; j++) { H[j][n-1] = H[j][n-1] / t; H[j][n] = H[j][n] / t; } } } } } } // Vectors of isolated roots for (int i = 0; i < nn; i++) { if (i < low || i > high) { for (int j = i; j < nn; j++) { V[i][j] = H[i][j]; } } } // Back transformation to get eigenvectors of original matrix for (int j = nn-1; j >= low; j--) { for (int i = low; i <= high; i++) { z = Real(0.0); for (int k = low; k <= min(j,high); k++) { z = z + V[i][k] * H[k][j]; } V[i][j] = z; } } } public: /** Check for symmetry, then construct the eigenvalue decomposition @param A Square real (non-complex) matrix */ Eigenvalue(const Matrix &A) { n = A.num_cols(); V = Matrix(n,n); d = Vector(n); e = Vector(n); issymmetric = 1; for (int j = 0; (j < n) && issymmetric; j++) { for (int i = 0; (i < n) && issymmetric; i++) { issymmetric = (A[i][j] == A[j][i]); } } if (issymmetric) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { V[i][j] = A[i][j]; } } // Tridiagonalize. tred2(); // Diagonalize. tql2(); } else { H = Matrix(n,n); ort = Vector(n); for (int j = 0; j < n; j++) { for (int i = 0; i < n; i++) { H[i][j] = A[i][j]; } } // Reduce to Hessenberg form. orthes(); // Reduce Hessenberg to real Schur form. hqr2(); } } /** Return the eigenvector matrix @return V */ void getV (Matrix &V_) { V_ = V; return; } /** Return the real parts of the eigenvalues @return real(diag(D)) */ void getRealEigenvalues (Vector &d_) { d_ = d; return ; } /** Return the imaginary parts of the eigenvalues in parameter e_. @param e_: new matrix with imaginary parts of the eigenvalues. */ void getImagEigenvalues (Vector &e_) { e_ = e; return; } /** Computes the block diagonal eigenvalue matrix. If the original matrix A is not symmetric, then the eigenvalue matrix D is block diagonal with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues, a + i*b, in 2-by-2 blocks, [a, b; -b, a]. That is, if the complex eigenvalues look like


          u + iv     .        .          .      .    .
            .      u - iv     .          .      .    .
            .        .      a + ib       .      .    .
            .        .        .        a - ib   .    .
            .        .        .          .      x    .
            .        .        .          .      .    y
then D looks like

            u        v        .          .      .    .
           -v        u        .          .      .    . 
            .        .        a          b      .    .
            .        .       -b          a      .    .
            .        .        .          .      x    .
            .        .        .          .      .    y
This keeps V a real matrix in both symmetric and non-symmetric cases, and A*V = V*D. @param D: upon return, the matrix is filled with the block diagonal eigenvalue matrix. */ void getD (Matrix &D) { D = Matrix(n,n); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { D[i][j] = Real(0.0); } D[i][i] = d[i]; if (e[i] > 0) { D[i][i+1] = e[i]; } else if (e[i] < 0) { D[i][i-1] = e[i]; } } } }; /** LU Decomposition.

For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n unit lower triangular matrix L, an n-by-n upper triangular matrix U, and a permutation vector piv of length m so that A(piv,:) = L*U. If m < n, then L is m-by-m and U is m-by-n.

The LU decompostion with pivoting always exists, even if the matrix is singular, so the constructor will never fail. The primary use of the LU decomposition is in the solution of square systems of simultaneous linear equations. This will fail if isNonsingular() returns false. */ template class LU { private: /* Array for internal storage of decomposition. */ Matrix LU_; int m, n, pivsign; Vector piv; Matrix permute_copy( const Matrix &A, const Vector &piv, int j0, int j1) { int piv_length = piv.dim(); Matrix X(piv_length, j1-j0+1); for (int i = 0; i < piv_length; i++) for (int j = j0; j <= j1; j++) X[i][j-j0] = A[piv[i]][j]; return X; } Vector permute_copy( const Vector &A, const Vector &piv) { int piv_length = piv.dim(); if (piv_length != A.dim()) return Vector(); Vector x(piv_length); for (int i = 0; i < piv_length; i++) x[i] = A[piv[i]]; return x; } public : /** LU Decomposition @param A Rectangular matrix @return LU Decomposition object to access L, U and piv. */ LU (const Matrix &A) : LU_(A), m(A.num_rows()), n(A.num_cols()), piv(A.num_rows()) { // Use a "left-looking", dot-product, Crout/Doolittle algorithm. for (int i = 0; i < m; i++) { piv[i] = i; } pivsign = 1; Real *LUrowi = 0;; Vector LUcolj(m); // Outer loop. for (int j = 0; j < n; j++) { // Make a copy of the j-th column to localize references. for (int i = 0; i < m; i++) { LUcolj[i] = LU_[i][j]; } // Apply previous transformations. for (int i = 0; i < m; i++) { LUrowi = LU_[i]; // Most of the time is spent in the following dot product. int kmax = min(i,j); double s = Real(0.0); for (int k = 0; k < kmax; k++) { s += LUrowi[k]*LUcolj[k]; } LUrowi[j] = LUcolj[i] -= s; } // Find pivot and exchange if necessary. int p = j; for (int i = j+1; i < m; i++) { if (abs(LUcolj[i]) > abs(LUcolj[p])) { p = i; } } if (p != j) { int k=0; for (k = 0; k < n; k++) { double t = LU_[p][k]; LU_[p][k] = LU_[j][k]; LU_[j][k] = t; } k = piv[p]; piv[p] = piv[j]; piv[j] = k; pivsign = -pivsign; } // Compute multipliers. if ((j < m) && (LU_[j][j] != Real(0.0))) { for (int i = j+1; i < m; i++) { LU_[i][j] /= LU_[j][j]; } } } } /** Is the matrix nonsingular? @return 1 (true) if upper triangular factor U (and hence A) is nonsingular, 0 otherwise. */ int isNonsingular () { for (int j = 0; j < n; j++) { if (LU_[j][j] == 0) return 0; } return 1; } /** Return lower triangular factor @return L */ Matrix getL () { Matrix L_(m,n); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (i > j) { L_[i][j] = LU_[i][j]; } else if (i == j) { L_[i][j] = Real(1.0); } else { L_[i][j] = Real(0.0); } } } return L_; } /** Return upper triangular factor @return U portion of LU factorization. */ Matrix getU () { Matrix U_(n,n); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i <= j) { U_[i][j] = LU_[i][j]; } else { U_[i][j] = Real(0.0); } } } return U_; } /** Return pivot permutation vector @return piv */ Vector getPivot () { return piv; } /** Compute determinant using LU factors. @return determinant of A, or 0 if A is not square. */ Real det () { if (m != n) { return Real(0); } Real d = Real(pivsign); for (int j = 0; j < n; j++) { d *= LU_[j][j]; } return d; } /** Solve A*X = B @param B A Matrix with as many rows as A and any number of columns. @return X so that L*U*X = B(piv,:), if B is nonconformant, returns Real(0.0) (null) array. */ Matrix solve (const Matrix &B) { /* Dimensions: A is mxn, X is nxk, B is mxk */ if (B.num_rows() != m) { return Matrix(Real(0.0)); } if (!isNonsingular()) { return Matrix(Real(0.0)); } // Copy right hand side with pivoting int nx = B.num_cols(); Matrix X = permute_copy(B, piv, 0, nx-1); // Solve L*Y = B(piv,:) for (int k = 0; k < n; k++) { for (int i = k+1; i < n; i++) { for (int j = 0; j < nx; j++) { X[i][j] -= X[k][j]*LU_[i][k]; } } } // Solve U*X = Y; for (int k = n-1; k >= 0; k--) { for (int j = 0; j < nx; j++) { X[k][j] /= LU_[k][k]; } for (int i = 0; i < k; i++) { for (int j = 0; j < nx; j++) { X[i][j] -= X[k][j]*LU_[i][k]; } } } return X; } /** Solve A*x = b, where x and b are vectors of length equal to the number of rows in A. @param b a vector (Vector> of length equal to the first dimension of A. @return x a vector (Vector> so that L*U*x = b(piv), if B is nonconformant, returns Real(0.0) (null) array. */ Vector solve (const Vector &b) { /* Dimensions: A is mxn, X is nxk, B is mxk */ if (b.dim() != m) { return Vector(); } if (!isNonsingular()) { return Vector(); } Vector x = permute_copy(b, piv); // Solve L*Y = B(piv) for (int k = 0; k < n; k++) { for (int i = k+1; i < n; i++) { x[i] -= x[k]*LU_[i][k]; } } // Solve U*X = Y; for (int k = n-1; k >= 0; k--) { x[k] /= LU_[k][k]; for (int i = 0; i < k; i++) x[i] -= x[k]*LU_[i][k]; } return x; } }; /* class LU */ /**

Classical QR Decompisition: for an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n orthogonal matrix Q and an n-by-n upper triangular matrix R so that A = Q*R.

The QR decompostion always exists, even if the matrix does not have full rank, so the constructor will never fail. The primary use of the QR decomposition is in the least squares solution of nonsquare systems of simultaneous linear equations. This will fail if isFullRank() returns 0 (false).

The Q and R factors can be retrived via the getQ() and getR() methods. Furthermore, a solve() method is provided to find the least squares solution of Ax=b using the QR factors.

(Adapted from JAMA, a Java Matrix Library, developed by jointly by the Mathworks and NIST; see http://math.nist.gov/javanumerics/jama). */ template class QR { /* Array for internal storage of decomposition. @serial internal array storage. */ Matrix QR_; /* Row and column dimensions. @serial column dimension. @serial row dimension. */ int m, n; /* Array for internal storage of diagonal of R. @serial diagonal of R. */ Vector Rdiag; public: /** Create a QR factorization object for A. @param A rectangular (m>=n) matrix. */ QR(const Matrix &A) /* constructor */ { QR_ = A; m = A.num_rows(); n = A.num_cols(); Rdiag = Vector(n); int i=0, j=0, k=0; // Main loop. for (k = 0; k < n; k++) { // Compute 2-norm of k-th column without under/overflow. Real nrm = 0; for (i = k; i < m; i++) { nrm = hypot(nrm,QR_[i][k]); } if (nrm != Real(0.0)) { // Form k-th Householder vector. if (QR_[k][k] < 0) { nrm = -nrm; } for (i = k; i < m; i++) { QR_[i][k] /= nrm; } QR_[k][k] += Real(1.0); // Apply transformation to remaining columns. for (j = k+1; j < n; j++) { Real s = Real(0.0); for (i = k; i < m; i++) { s += QR_[i][k]*QR_[i][j]; } s = -s/QR_[k][k]; for (i = k; i < m; i++) { QR_[i][j] += s*QR_[i][k]; } } } Rdiag[k] = -nrm; } } /** Flag to denote the matrix is of full rank. @return 1 if matrix is full rank, 0 otherwise. */ int isFullRank() const { for (int j = 0; j < n; j++) { if (Rdiag[j] == 0) return 0; } return 1; } /** Retreive the Householder vectors from QR factorization @returns lower trapezoidal matrix whose columns define the reflections */ Matrix getHouseholder (void) const { Matrix H(m,n); /* note: H is completely filled in by algorithm, so initializaiton of H is not necessary. */ for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (i >= j) { H[i][j] = QR_[i][j]; } else { H[i][j] = Real(0.0); } } } return H; } /** Return the upper triangular factor, R, of the QR factorization @return R */ Matrix getR() const { Matrix R(n,n); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i < j) { R[i][j] = QR_[i][j]; } else if (i == j) { R[i][j] = Rdiag[i]; } else { R[i][j] = Real(0.0); } } } return R; } /** @return Q the (ecnomy-sized) orthogonal factor (Q*R=A). */ Matrix getQ() const { int i=0, j=0, k=0; Matrix Q(m,n); for (k = n-1; k >= 0; k--) { for (i = 0; i < m; i++) { Q[i][k] = Real(0.0); } Q[k][k] = Real(1.0); for (j = k; j < n; j++) { if (QR_[k][k] != 0) { Real s = Real(0.0); for (i = k; i < m; i++) { s += QR_[i][k]*Q[i][j]; } s = -s/QR_[k][k]; for (i = k; i < m; i++) { Q[i][j] += s*QR_[i][k]; } } } } return Q; } /** Least squares solution of A*x = b @param b right hand side (m-length vector). @return x n-length vector that minimizes the two norm of Q*R*X-B. If B is non-conformant, or if QR.isFullRank() is false, the routine returns a null (0-length) vector. */ Vector solve(const Vector &b) const { if (b.dim() != m) /* arrays must be conformant */ return Vector(); if ( !isFullRank() ) /* matrix is rank deficient */ { return Vector(); } Vector x = b; // Compute Y = transpose(Q)*b for (int k = 0; k < n; k++) { Real s = Real(0.0); for (int i = k; i < m; i++) { s += QR_[i][k]*x[i]; } s = -s/QR_[k][k]; for (int i = k; i < m; i++) { x[i] += s*QR_[i][k]; } } // Solve R*X = Y; for (int k = n-1; k >= 0; k--) { x[k] /= Rdiag[k]; for (int i = 0; i < k; i++) { x[i] -= x[k]*QR_[i][k]; } } /* return n x nx portion of X */ Vector x_(n); for (int i=0; i solve(const Matrix &B) const { if (B.num_rows() != m) /* arrays must be conformant */ return Matrix(Real(0.0)); if ( !isFullRank() ) /* matrix is rank deficient */ { return Matrix(Real(0.0)); } int nx = B.num_cols(); Matrix X = B; int i=0, j=0, k=0; // Compute Y = transpose(Q)*B for (k = 0; k < n; k++) { for (j = 0; j < nx; j++) { Real s = Real(0.0); for (i = k; i < m; i++) { s += QR_[i][k]*X[i][j]; } s = -s/QR_[k][k]; for (i = k; i < m; i++) { X[i][j] += s*QR_[i][k]; } } } // Solve R*X = Y; for (k = n-1; k >= 0; k--) { for (j = 0; j < nx; j++) { X[k][j] /= Rdiag[k]; } for (i = 0; i < k; i++) { for (j = 0; j < nx; j++) { X[i][j] -= X[k][j]*QR_[i][k]; } } } /* return n x nx portion of X */ Matrix X_(n,nx); for (i=0; i For an m-by-n matrix A with m >= n, the singular value decomposition is an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and an n-by-n orthogonal matrix V so that A = U*S*V'.

The singular values, sigma[k] = S[k][k], are ordered so that sigma[0] >= sigma[1] >= ... >= sigma[n-1].

The singular value decompostion always exists, so the constructor will never fail. The matrix condition number and the effective numerical rank can be computed from this decomposition.

(Adapted from JAMA, a Java Matrix Library, developed by jointly by the Mathworks and NIST; see http://math.nist.gov/javanumerics/jama). */ template class SVD { Matrix U, V; Vector s; int m, n; public: SVD (const Matrix &Arg) { m = Arg.num_rows(); n = Arg.num_cols(); int nu = min(m,n); s = Vector(min(m+1,n)); U = Matrix(m, nu, Real(0)); V = Matrix(n,n); Vector e(n); Vector work(m); Matrix A(Arg); int wantu = 1; /* boolean */ int wantv = 1; /* boolean */ int i=0, j=0, k=0; // Reduce A to bidiagonal form, storing the diagonal elements // in s and the super-diagonal elements in e. int nct = min(m-1,n); int nrt = max(0,min(n-2,m)); for (k = 0; k < max(nct,nrt); k++) { if (k < nct) { // Compute the transformation for the k-th column and // place the k-th diagonal in s[k]. // Compute 2-norm of k-th column without under/overflow. s[k] = 0; for (i = k; i < m; i++) { s[k] = hypot(s[k],A[i][k]); } if (s[k] != Real(0.0)) { if (A[k][k] < Real(0.0)) { s[k] = -s[k]; } for (i = k; i < m; i++) { A[i][k] /= s[k]; } A[k][k] += Real(1.0); } s[k] = -s[k]; } for (j = k+1; j < n; j++) { if ((k < nct) && (s[k] != Real(0.0))) { // Apply the transformation. double t = 0; for (i = k; i < m; i++) { t += A[i][k]*A[i][j]; } t = -t/A[k][k]; for (i = k; i < m; i++) { A[i][j] += t*A[i][k]; } } // Place the k-th row of A into e for the // subsequent calculation of the row transformation. e[j] = A[k][j]; } if (wantu & (k < nct)) { // Place the transformation in U for subsequent back // multiplication. for (i = k; i < m; i++) { U[i][k] = A[i][k]; } } if (k < nrt) { // Compute the k-th row transformation and place the // k-th super-diagonal in e[k]. // Compute 2-norm without under/overflow. e[k] = 0; for (i = k+1; i < n; i++) { e[k] = hypot(e[k],e[i]); } if (e[k] != Real(0.0)) { if (e[k+1] < Real(0.0)) { e[k] = -e[k]; } for (i = k+1; i < n; i++) { e[i] /= e[k]; } e[k+1] += Real(1.0); } e[k] = -e[k]; if ((k+1 < m) & (e[k] != Real(0.0))) { // Apply the transformation. for (i = k+1; i < m; i++) { work[i] = Real(0.0); } for (j = k+1; j < n; j++) { for (i = k+1; i < m; i++) { work[i] += e[j]*A[i][j]; } } for (j = k+1; j < n; j++) { double t = -e[j]/e[k+1]; for (i = k+1; i < m; i++) { A[i][j] += t*work[i]; } } } if (wantv) { // Place the transformation in V for subsequent // back multiplication. for (i = k+1; i < n; i++) { V[i][k] = e[i]; } } } } // Set up the final bidiagonal matrix or order p. int p = min(n,m+1); if (nct < n) { s[nct] = A[nct][nct]; } if (m < p) { s[p-1] = Real(0.0); } if (nrt+1 < p) { e[nrt] = A[nrt][p-1]; } e[p-1] = Real(0.0); // If required, generate U. if (wantu) { for (j = nct; j < nu; j++) { for (i = 0; i < m; i++) { U[i][j] = Real(0.0); } U[j][j] = Real(1.0); } for (k = nct-1; k >= 0; k--) { if (s[k] != Real(0.0)) { for (j = k+1; j < nu; j++) { double t = 0; for (i = k; i < m; i++) { t += U[i][k]*U[i][j]; } t = -t/U[k][k]; for (i = k; i < m; i++) { U[i][j] += t*U[i][k]; } } for (i = k; i < m; i++ ) { U[i][k] = -U[i][k]; } U[k][k] = Real(1.0) + U[k][k]; for (i = 0; i < k-1; i++) { U[i][k] = Real(0.0); } } else { for (i = 0; i < m; i++) { U[i][k] = Real(0.0); } U[k][k] = Real(1.0); } } } // If required, generate V. if (wantv) { for (k = n-1; k >= 0; k--) { if ((k < nrt) & (e[k] != Real(0.0))) { for (j = k+1; j < nu; j++) { double t = 0; for (i = k+1; i < n; i++) { t += V[i][k]*V[i][j]; } t = -t/V[k+1][k]; for (i = k+1; i < n; i++) { V[i][j] += t*V[i][k]; } } } for (i = 0; i < n; i++) { V[i][k] = Real(0.0); } V[k][k] = Real(1.0); } } // Main iteration loop for the singular values. int pp = p-1; int iter = 0; double eps = pow(2.0,-52.0); while (p > 0) { int k=0; int kase=0; // Here is where a test for too many iterations would go. // This section of the program inspects for // negligible elements in the s and e arrays. On // completion the variables kase and k are set as follows. // kase = 1 if s(p) and e[k-1] are negligible and k

= -1; k--) { if (k == -1) { break; } if (abs(e[k]) <= eps*(abs(s[k]) + abs(s[k+1]))) { e[k] = Real(0.0); break; } } if (k == p-2) { kase = 4; } else { int ks; for (ks = p-1; ks >= k; ks--) { if (ks == k) { break; } double t = (ks != p ? abs(e[ks]) : 0.) + (ks != k+1 ? abs(e[ks-1]) : 0.); if (abs(s[ks]) <= eps*t) { s[ks] = Real(0.0); break; } } if (ks == k) { kase = 3; } else if (ks == p-1) { kase = 1; } else { kase = 2; k = ks; } } k++; // Perform the task indicated by kase. switch (kase) { // Deflate negligible s(p). case 1: { double f = e[p-2]; e[p-2] = Real(0.0); for (j = p-2; j >= k; j--) { double t = hypot(s[j],f); double cs = s[j]/t; double sn = f/t; s[j] = t; if (j != k) { f = -sn*e[j-1]; e[j-1] = cs*e[j-1]; } if (wantv) { for (i = 0; i < n; i++) { t = cs*V[i][j] + sn*V[i][p-1]; V[i][p-1] = -sn*V[i][j] + cs*V[i][p-1]; V[i][j] = t; } } } } break; // Split at negligible s(k). case 2: { double f = e[k-1]; e[k-1] = Real(0.0); for (j = k; j < p; j++) { double t = hypot(s[j],f); double cs = s[j]/t; double sn = f/t; s[j] = t; f = -sn*e[j]; e[j] = cs*e[j]; if (wantu) { for (i = 0; i < m; i++) { t = cs*U[i][j] + sn*U[i][k-1]; U[i][k-1] = -sn*U[i][j] + cs*U[i][k-1]; U[i][j] = t; } } } } break; // Perform one qr step. case 3: { // Calculate the shift. double scale = max(max(max(max( abs(s[p-1]),abs(s[p-2])),abs(e[p-2])), abs(s[k])),abs(e[k])); double sp = s[p-1]/scale; double spm1 = s[p-2]/scale; double epm1 = e[p-2]/scale; double sk = s[k]/scale; double ek = e[k]/scale; double b = ((spm1 + sp)*(spm1 - sp) + epm1*epm1)/2.0; double c = (sp*epm1)*(sp*epm1); double shift = Real(0.0); if ((b != Real(0.0)) || (c != Real(0.0))) { shift = sqrt(b*b + c); if (b < Real(0.0)) { shift = -shift; } shift = c/(b + shift); } double f = (sk + sp)*(sk - sp) + shift; double g = sk*ek; // Chase zeros. for (j = k; j < p-1; j++) { double t = hypot(f,g); double cs = f/t; double sn = g/t; if (j != k) { e[j-1] = t; } f = cs*s[j] + sn*e[j]; e[j] = cs*e[j] - sn*s[j]; g = sn*s[j+1]; s[j+1] = cs*s[j+1]; if (wantv) { for (i = 0; i < n; i++) { t = cs*V[i][j] + sn*V[i][j+1]; V[i][j+1] = -sn*V[i][j] + cs*V[i][j+1]; V[i][j] = t; } } t = hypot(f,g); cs = f/t; sn = g/t; s[j] = t; f = cs*e[j] + sn*s[j+1]; s[j+1] = -sn*e[j] + cs*s[j+1]; g = sn*e[j+1]; e[j+1] = cs*e[j+1]; if (wantu && (j < m-1)) { for (i = 0; i < m; i++) { t = cs*U[i][j] + sn*U[i][j+1]; U[i][j+1] = -sn*U[i][j] + cs*U[i][j+1]; U[i][j] = t; } } } e[p-2] = f; iter = iter + 1; } break; // Convergence. case 4: { // Make the singular values positive. if (s[k] <= Real(0.0)) { s[k] = (s[k] < Real(0.0) ? -s[k] : Real(0.0)); if (wantv) { for (i = 0; i <= pp; i++) { V[i][k] = -V[i][k]; } } } // Order the singular values. while (k < pp) { if (s[k] >= s[k+1]) { break; } double t = s[k]; s[k] = s[k+1]; s[k+1] = t; if (wantv && (k < n-1)) { for (i = 0; i < n; i++) { t = V[i][k+1]; V[i][k+1] = V[i][k]; V[i][k] = t; } } if (wantu && (k < m-1)) { for (i = 0; i < m; i++) { t = U[i][k+1]; U[i][k+1] = U[i][k]; U[i][k] = t; } } k++; } iter = 0; p--; } break; } } } void getU (Matrix &A) { int minm = min(m+1,n); A = Matrix(m, minm); for (int i=0; i &A) { A = V; } /** Return the one-dimensional array of singular values */ void getSingularValues (Vector &x) { x = s; } /** Return the diagonal matrix of singular values @return S */ void getS (Matrix &A) { A = Matrix(n,n); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { A[i][j] = Real(0.0); } A[i][i] = s[i]; } } /** Two norm (max(S)) */ double norm2 () { return s[0]; } /** Two norm of condition number (max(S)/min(S)) */ double cond () { return s[0]/s[min(m,n)-1]; } /** Effective numerical matrix rank @return Number of nonnegligible singular values. */ int rank () { double eps = pow(2.0,-52.0); double tol = max(m,n)*s[0]*eps; int r = 0; for (int i = 0; i < s.dim(); i++) { if (s[i] > tol) { r++; } } return r; } }; } /* namespace Linear_Algebra */ } /* namespace TNT */ #endif // TNT_LINALG_H_