You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
3.3 KiB
149 lines
3.3 KiB
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;
|
|
}
|
|
}
|
|
|