/* * Apache License * Version 2.0, January 2004 * http://www.apache.org/licenses/ * * Copyright 2013 Aurelian Tutuianu * Copyright 2014 Aurelian Tutuianu * Copyright 2015 Aurelian Tutuianu * Copyright 2016 Aurelian Tutuianu * * 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 rapaio.math.linear.dense; import rapaio.math.linear.RM; import rapaio.math.linear.RV; import rapaio.util.Pair; import java.io.Serializable; /** * QR Decomposition. * <p> * 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 rapaio.data.matrix R so that * A = Q*R. * <p> * 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 non square systems * of simultaneous linear equations. This will fail if isFullRank() * returns false. * <p> * User: Aurelian Tutuianu <padreati@yahoo.com> */ @Deprecated public class QR implements Serializable { private static final long serialVersionUID = -8322866575684242727L; private RM QR; private RV Rdiag; public QR(RM A) { // Initialize. QR = A.solidCopy(); Rdiag = SolidRV.empty(QR.colCount()); // Main loop. for (int k = 0; k < QR.colCount(); k++) { // Compute 2-norm of k-th column without under/overflow. double nrm = 0; for (int i = k; i < QR.rowCount(); i++) { nrm = StrictMath.hypot(nrm, QR.get(i, k)); } if (nrm != 0.0) { // Form k-th Householder var. if (QR.get(k, k) < 0) { nrm = -nrm; } for (int i = k; i < QR.rowCount(); i++) { QR.set(i, k, QR.get(i, k) / nrm); } QR.set(k, k, QR.get(k, k) + 1.0); // Apply transformation to remaining columns. for (int j = k + 1; j < QR.colCount(); j++) { double s = 0.0; for (int i = k; i < QR.rowCount(); i++) { s += QR.get(i, k) * QR.get(i, j); } s = -s / QR.get(k, k); for (int i = k; i < QR.rowCount(); i++) { QR.set(i, j, QR.get(i, j) + s * QR.get(i, k)); } } } Rdiag.increment(k, -nrm); } } /** * Is the matrix full rank? * * @return true if R, and hence A, has full rank. */ public boolean isFullRank() { for (int j = 0; j < QR.colCount(); j++) { if (Rdiag.get(j) == 0) return false; } return true; } /** * Return the Householder vectors * * @return Lower trapezoidal matrix whose columns define the reflections */ public RM getH() { RM H = SolidRM.empty(QR.rowCount(), QR.colCount()); for (int i = 0; i < QR.rowCount(); i++) { for (int j = 0; j < QR.colCount(); j++) { if (i >= j) { H.set(i, j, QR.get(i, j)); } else { H.set(i, j, 0.0); } } } return H; } /** * Return the upper triangular factor * * @return R */ public RM getR() { RM R = SolidRM.empty(QR.colCount(), QR.colCount()); for (int i = 0; i < QR.colCount(); i++) { for (int j = 0; j < QR.colCount(); j++) { if (i < j) { R.set(i, j, QR.get(i, j)); } else if (i == j) { R.set(i, j, Rdiag.get(i)); } else { R.set(i, j, 0.0); } } } return R; } /** * Generate and return the (economy-sized) orthogonal factor * * @return Q */ public RM getQ() { RM Q = SolidRM.empty(QR.rowCount(), QR.colCount()); for (int k = QR.colCount() - 1; k >= 0; k--) { for (int i = 0; i < QR.rowCount(); i++) { Q.set(i, k, 0.0); } Q.set(k, k, 1.0); for (int j = k; j < QR.colCount(); j++) { if (QR.get(k, k) != 0) { double s = 0.0; for (int i = k; i < QR.rowCount(); i++) { s += QR.get(i, k) * Q.get(i, j); } s = -s / QR.get(k, k); for (int i = k; i < QR.rowCount(); i++) { Q.set(i, j, Q.get(i, j) + s * QR.get(i, k)); } } } } return Q; } public RM getQR() { return QR.solidCopy(); } public Pair<RM, RM> getPairQR() { return Pair.from(getQ(), getR()); } /** * Least squares solution of A*X = B * * @param B A Matrix with as many rows as A and any number of columns. * @return X that minimizes the two norm of Q*R*X-B. * @throws IllegalArgumentException Matrix row dimensions must agree. * @throws RuntimeException Matrix is rank deficient. */ public RM solve(RM B) { if (B.rowCount() != QR.rowCount()) { throw new IllegalArgumentException("Matrix row dimensions must agree."); } if (!isFullRank()) { throw new RuntimeException("Matrix is rank deficient."); } // Copy right hand side RM X = B.solidCopy(); // Compute Y = transpose(Q)*B for (int k = 0; k < QR.colCount(); k++) { for (int j = 0; j < B.colCount(); j++) { double s = 0.0; for (int i = k; i < QR.rowCount(); i++) { s += QR.get(i, k) * X.get(i, j); } s = -s / QR.get(k, k); for (int i = k; i < QR.rowCount(); i++) { X.set(i, j, X.get(i, j) + s * QR.get(i, k)); } } } // Solve R*X = Y; for (int k = QR.colCount() - 1; k >= 0; k--) { for (int j = 0; j < B.colCount(); j++) { X.set(k, j, X.get(k, j) / Rdiag.get(k)); } for (int i = 0; i < k; i++) { for (int j = 0; j < B.colCount(); j++) { X.set(i, j, X.get(i, j) - X.get(k, j) * QR.get(i, k)); } } } return X.rangeRows(0, QR.colCount()).rangeCols(0, B.colCount()); } }