import Matrix from '../matrix'; import WrapperMatrix2D from '../wrap/WrapperMatrix2D'; import { hypotenuse } from './util'; export default class QrDecomposition { constructor(value) { value = WrapperMatrix2D.checkMatrix(value); let qr = value.clone(); let m = value.rows; let n = value.columns; let rdiag = new Float64Array(n); let i, j, k, s; for (k = 0; k < n; k++) { let nrm = 0; for (i = k; i < m; i++) { nrm = hypotenuse(nrm, qr.get(i, k)); } if (nrm !== 0) { if (qr.get(k, k) < 0) { nrm = -nrm; } for (i = k; i < m; i++) { qr.set(i, k, qr.get(i, k) / nrm); } qr.set(k, k, qr.get(k, k) + 1); for (j = k + 1; j < n; j++) { s = 0; for (i = k; i < m; i++) { s += qr.get(i, k) * qr.get(i, j); } s = -s / qr.get(k, k); for (i = k; i < m; i++) { qr.set(i, j, qr.get(i, j) + s * qr.get(i, k)); } } } rdiag[k] = -nrm; } this.QR = qr; this.Rdiag = rdiag; } solve(value) { value = Matrix.checkMatrix(value); let qr = this.QR; let m = qr.rows; if (value.rows !== m) { throw new Error('Matrix row dimensions must agree'); } if (!this.isFullRank()) { throw new Error('Matrix is rank deficient'); } let count = value.columns; let X = value.clone(); let n = qr.columns; let i, j, k, s; for (k = 0; k < n; k++) { for (j = 0; j < count; j++) { s = 0; for (i = k; i < m; i++) { s += qr.get(i, k) * X.get(i, j); } s = -s / qr.get(k, k); for (i = k; i < m; i++) { X.set(i, j, X.get(i, j) + s * qr.get(i, k)); } } } for (k = n - 1; k >= 0; k--) { for (j = 0; j < count; j++) { X.set(k, j, X.get(k, j) / this.Rdiag[k]); } for (i = 0; i < k; i++) { for (j = 0; j < count; j++) { X.set(i, j, X.get(i, j) - X.get(k, j) * qr.get(i, k)); } } } return X.subMatrix(0, n - 1, 0, count - 1); } isFullRank() { let columns = this.QR.columns; for (let i = 0; i < columns; i++) { if (this.Rdiag[i] === 0) { return false; } } return true; } get upperTriangularMatrix() { let qr = this.QR; let n = qr.columns; let X = new Matrix(n, n); let i, j; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { if (i < j) { X.set(i, j, qr.get(i, j)); } else if (i === j) { X.set(i, j, this.Rdiag[i]); } else { X.set(i, j, 0); } } } return X; } get orthogonalMatrix() { let qr = this.QR; let rows = qr.rows; let columns = qr.columns; let X = new Matrix(rows, columns); let i, j, k, s; for (k = columns - 1; k >= 0; k--) { for (i = 0; i < rows; i++) { X.set(i, k, 0); } X.set(k, k, 1); for (j = k; j < columns; j++) { if (qr.get(k, k) !== 0) { s = 0; for (i = k; i < rows; i++) { s += qr.get(i, k) * X.get(i, j); } s = -s / qr.get(k, k); for (i = k; i < rows; i++) { X.set(i, j, X.get(i, j) + s * qr.get(i, k)); } } } } return X; } }