mikera.matrixx.decompose.impl.hessenberg.HessenbergSimilarDecomposition Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vectorz Show documentation
Show all versions of vectorz Show documentation
Fast double-precision vector and matrix maths library for Java, supporting N-dimensional numeric arrays.
/*
* Copyright (c) 2009-2013, Peter Abeles. All Rights Reserved.
*
* This file is part of Efficient Java Matrix Library (EJML).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package mikera.matrixx.decompose.impl.hessenberg;
import mikera.matrixx.AMatrix;
import mikera.matrixx.Matrix;
import mikera.matrixx.decompose.impl.qr.QRHelperFunctions;
/**
*
* Finds the decomposition of a matrix in the form of:
*
* A = QHQT
*
* where A is an m by m matrix, Q is an orthogonal matrix, and H is an upper Hessenberg matrix.
*
*
*
* A matrix is upper Hessenberg if aij = 0 for all i > j+1.
*
*
*
* This decomposition is primarily used as a step for computing the eigenvalue decomposition of a matrix.
* The basic algorithm comes from David S. Watkins, "Fundamentals of MatrixComputations" Second Edition.
*
*/
// TODO create a column based one similar to what was done for QR decomposition?
public class HessenbergSimilarDecomposition {
// A combined matrix that stores te upper Hessenberg matrix and the orthogonal matrix.
private Matrix QH;
// number of rows and columns of the matrix being decompose
private int N;
// the first element in the orthogonal vectors
private double gammas[];
// temporary storage
private double b[];
private double u[];
// a constructor for the static decompose method to use
private HessenbergSimilarDecomposition() {}
/**
* Computes the decomposition of the provided matrix. If no errors are detected then true is returned,
* false otherwise.
* @param A The matrix that is being decomposed. Not modified.
* @return If it detects any errors or not.
*/
public static HessenbergResult decompose( AMatrix A )
{
HessenbergSimilarDecomposition alg = new HessenbergSimilarDecomposition();
return alg._decompose(A);
}
/**
* The raw QH matrix that is stored internally.
*
* @return QH matrix.
*/
public AMatrix getQH() {
return QH;
}
/**
* An upper Hessenberg matrix from the decompostion.
*
* @return The extracted H matrix.
*/
private AMatrix getH() {
Matrix H = Matrix.create(N,N);
// copy the first row
System.arraycopy(QH.data, 0, H.data, 0, N);
for( int i = 1; i < N; i++ ) {
for( int j = i-1; j < N; j++ ) {
H.set(i,j, QH.get(i,j));
}
}
return H;
}
/**
* An orthogonal matrix that has the following property: H = QTAQ
*
* @return The extracted Q matrix.
*/
private AMatrix getQ() {
Matrix Q = Matrix.createIdentity(N);
for( int j = N-2; j >= 0; j-- ) {
u[j+1] = 1;
for( int i = j+2; i < N; i++ ) {
u[i] = QH.get(i,j);
}
QRHelperFunctions.rank1UpdateMultR(Q,u,gammas[j],j+1,j+1,N,b);
}
return Q;
}
/**
* Internal function for computing the decomposition.
* @param A
*/
private HessenbergResult _decompose(AMatrix A) {
if( A.rowCount() != A.columnCount() )
throw new IllegalArgumentException("A must be square.");
QH = A.copy().toMatrix();
N = A.columnCount();
b = new double[ N ];
gammas = new double[ N ];
u = new double[ N ];
double h[] = QH.data;
for( int k = 0; k < N-2; k++ ) {
// find the largest value in this column
// this is used to normalize the column and mitigate overflow/underflow
double max = 0;
for( int i = k+1; i < N; i++ ) {
// copy the householder vector to vector outside of the matrix to reduce caching issues
// big improvement on larger matrices and a relatively small performance hit on small matrices.
double val = u[i] = h[i*N+k];
val = Math.abs(val);
if( val > max )
max = val;
}
if( max > 0 ) {
// -------- set up the reflector Q_k
double tau = 0;
// normalize to reduce overflow/underflow
// and compute tau for the reflector
for( int i = k+1; i < N; i++ ) {
double val = u[i] /= max;
tau += val*val;
}
tau = Math.sqrt(tau);
if( u[k+1] < 0 )
tau = -tau;
// write the reflector into the lower left column of the matrix
double nu = u[k+1] + tau;
u[k+1] = 1.0;
for( int i = k+2; i < N; i++ ) {
h[i*N+k] = u[i] /= nu;
}
double gamma = nu/tau;
gammas[k] = gamma;
// ---------- multiply on the left by Q_k
QRHelperFunctions.rank1UpdateMultR(QH,u,gamma,k+1,k+1,N,b);
// ---------- multiply on the right by Q_k
QRHelperFunctions.rank1UpdateMultL(QH,u,gamma,0,k+1,N);
// since the first element in the householder vector is known to be 1
// store the full upper hessenberg
h[(k+1)*N+k] = -tau*max;
} else {
gammas[k] = 0;
}
}
return new HessenbergResult(getH(), getQ());
}
public double[] getGammas() {
return gammas;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy