src/eig.js
import transpose from './transpose';
import Complex from './Complex'
import neq from './neq'
import clone from './clone'
import norm2 from './norm2'
import div from './div'
import dim from './dim'
import dot from './dot'
import add from './add'
import getBlock from './getBlock'
import tensor from './tensor'
import identity from './identity'
import epsilon from './epsilon'
import sub from './sub'
import mul from './mul'
import diag from './diag'
import rep from './rep'
import neg from './neg'
import getDiag from './getDiag'
/**
* Eigenvalues of real matrices
*
* @param {any} A
* @param {any} maxiter
* @returns
*/
export default function eig(A, maxiter) {
var QH = toUpperHessenberg(A);
var QB = QRFrancis(QH.H, maxiter);
var n = A.length,
i, k, flag = false,
B = QB.B,
H = dot(QB.Q, dot(QH.H, transpose(QB.Q)));
var Q = new Complex(dot(QB.Q, QH.Q)),
Q0;
var m = B.length,
j;
var a, b, c, d, p1, p2, disc, x, y, p, q, n1, n2;
var sqrt = Math.sqrt;
for (k = 0; k < m; k++) {
i = B[k][0];
if (i === B[k][1]) {
// nothing
} else {
j = i + 1;
a = H[i][i];
b = H[i][j];
c = H[j][i];
d = H[j][j];
if (b === 0 && c === 0) continue;
p1 = -a - d;
p2 = a * d - b * c;
disc = p1 * p1 - 4 * p2;
if (disc >= 0) {
if (p1 < 0) x = -0.5 * (p1 - sqrt(disc));
else x = -0.5 * (p1 + sqrt(disc));
n1 = (a - x) * (a - x) + b * b;
n2 = c * c + (d - x) * (d - x);
if (n1 > n2) {
n1 = sqrt(n1);
p = (a - x) / n1;
q = b / n1;
} else {
n2 = sqrt(n2);
p = c / n2;
q = (d - x) / n2;
}
Q0 = new Complex([
[q, -p],
[p, q]
]);
Q.setRows(i, j, dot(Q0, Q.getRows(i, j)));
} else {
x = -0.5 * p1;
y = 0.5 * sqrt(-disc);
n1 = (a - x) * (a - x) + b * b;
n2 = c * c + (d - x) * (d - x);
if (n1 > n2) {
n1 = sqrt(n1 + y * y);
p = (a - x) / n1;
q = b / n1;
x = 0;
y /= n1;
} else {
n2 = sqrt(n2 + y * y);
p = c / n2;
q = (d - x) / n2;
x = y / n2;
y = 0;
}
Q0 = new Complex([
[q, -p],
[p, q]
], [
[x, y],
[y, -x]
]);
Q.setRows(i, j, dot(Q0, Q.getRows(i, j)));
}
}
}
var R = dot(dot(Q, A), Q.transjugate()),
n = A.length,
E = new Complex(identity(n));
for (j = 0; j < n; j++) {
if (j > 0) {
for (k = j - 1; k >= 0; k--) {
var Rk = R.get([k, k]),
Rj = R.get([j, j]);
if (neq(Rk.re, Rj.re) || neq(Rk.im, Rj.im)) {
x = getBlock(R.getRow(k), [k], [j - 1]);
y = getBlock(E.getRow(j), [k], [j - 1]);
E.set([j, k], div((sub(neg(R.get([k, j])), dot(x, y))), sub(Rk, Rj)));
} else {
E.setRow(j, E.getRow(k));
continue;
}
}
}
}
for (j = 0; j < n; j++) {
x = E.getRow(j);
E.setRow(j, div(x, norm2(x)));
}
E = transpose(E);
E = dot(Q.transjugate(),E);
return {
lambda: getDiag(R),
E: E
};
};
function house(x) {
var v = clone(x);
var s = x[0] >= 0 ? 1 : -1;
var alpha = s * norm2(x);
v[0] += alpha;
var foo = norm2(v);
if (foo === 0) { /* this should not happen */
throw new Error('eig: internal error');
}
return div(v, foo);
}
function toUpperHessenberg(me) {
var s = dim(me);
if (s.length !== 2 || s[0] !== s[1]) {
throw new Error('mathlab: toUpperHessenberg() only works on square matrices');
}
var m = s[0],
i, j, k, x, v, A = clone(me),
B, C, Ai, Ci, Q = identity(m),
Qi;
for (j = 0; j < m - 2; j++) {
x = Array(m - j - 1);
for (i = j + 1; i < m; i++) {
x[i - j - 1] = A[i][j];
}
if (norm2(x) > 0) {
v = house(x);
B = getBlock(A, [j + 1, j], [m - 1, m - 1]);
C = tensor(v, dot(v, B));
for (i = j + 1; i < m; i++) {
Ai = A[i];
Ci = C[i - j - 1];
for (k = j; k < m; k++) Ai[k] -= 2 * Ci[k - j];
}
B = getBlock(A, [0, j + 1], [m - 1, m - 1]);
C = tensor(dot(B, v), v);
for (i = 0; i < m; i++) {
Ai = A[i];
Ci = C[i];
for (k = j + 1; k < m; k++) Ai[k] -= 2 * Ci[k - j - 1];
}
B = Array(m - j - 1);
for (i = j + 1; i < m; i++) B[i - j - 1] = Q[i];
C = tensor(v, dot(v, B));
for (i = j + 1; i < m; i++) {
Qi = Q[i];
Ci = C[i - j - 1];
for (k = 0; k < m; k++) Qi[k] -= 2 * Ci[k];
}
}
}
return {
H: A,
Q: Q
};
}
function QRFrancis(H, maxiter) {
if (typeof maxiter === "undefined") {
maxiter = 10000;
}
H = clone(H);
var H0 = clone(H);
var s = dim(H),
m = s[0],
x, v, a, b, c, d, det, tr, Hloc, Q = identity(m),
Qi, Hi, B, C, Ci, i, j, k, iter;
if (m < 3) {
return {
Q: Q,
B: [
[0, m - 1]
]
};
}
for (iter = 0; iter < maxiter; iter++) {
for (j = 0; j < m - 1; j++) {
if (Math.abs(H[j + 1][j]) < epsilon * (Math.abs(H[j][j]) + Math.abs(H[j + 1][j + 1]))) {
var QH1 = QRFrancis(getBlock(H, [0, 0], [j, j]), maxiter);
var QH2 = QRFrancis(getBlock(H, [j + 1, j + 1], [m - 1, m - 1]), maxiter);
B = Array(j + 1);
for (i = 0; i <= j; i++) {
B[i] = Q[i];
}
C = dot(QH1.Q, B);
for (i = 0; i <= j; i++) {
Q[i] = C[i];
}
B = Array(m - j - 1);
for (i = j + 1; i < m; i++) {
B[i - j - 1] = Q[i];
}
C = dot(QH2.Q, B);
for (i = j + 1; i < m; i++) {
Q[i] = C[i - j - 1];
}
return {
Q: Q,
B: QH1.B.concat(add(QH2.B, j + 1))
};
}
}
a = H[m - 2][m - 2];
b = H[m - 2][m - 1];
c = H[m - 1][m - 2];
d = H[m - 1][m - 1];
tr = a + d;
det = (a * d - b * c);
Hloc = getBlock(H, [0, 0], [2, 2]);
if (tr * tr >= 4 * det) {
var s1, s2;
s1 = 0.5 * (tr + Math.sqrt(tr * tr - 4 * det));
s2 = 0.5 * (tr - Math.sqrt(tr * tr - 4 * det));
Hloc = add(sub(dot(Hloc, Hloc),
mul(Hloc, s1 + s2)),
diag(rep([3], s1 * s2)));
} else {
Hloc = add(sub(dot(Hloc, Hloc),
mul(Hloc, tr)),
diag(rep([3], det)));
}
x = [Hloc[0][0], Hloc[1][0], Hloc[2][0]];
v = house(x);
B = [H[0], H[1], H[2]];
C = tensor(v, dot(v, B));
for (i = 0; i < 3; i++) {
Hi = H[i];
Ci = C[i];
for (k = 0; k < m; k++) Hi[k] -= 2 * Ci[k];
}
B = getBlock(H, [0, 0], [m - 1, 2]);
C = tensor(dot(B, v), v);
for (i = 0; i < m; i++) {
Hi = H[i];
Ci = C[i];
for (k = 0; k < 3; k++) Hi[k] -= 2 * Ci[k];
}
B = [Q[0], Q[1], Q[2]];
C = tensor(v, dot(v, B));
for (i = 0; i < 3; i++) {
Qi = Q[i];
Ci = C[i];
for (k = 0; k < m; k++) Qi[k] -= 2 * Ci[k];
}
var J;
for (j = 0; j < m - 2; j++) {
for (k = j; k <= j + 1; k++) {
if (Math.abs(H[k + 1][k]) < epsilon * (Math.abs(H[k][k]) + Math.abs(H[k + 1][k + 1]))) {
var QH1 = QRFrancis(getBlock(H, [0, 0], [k, k]), maxiter);
var QH2 = QRFrancis(getBlock(H, [k + 1, k + 1], [m - 1, m - 1]), maxiter);
B = Array(k + 1);
for (i = 0; i <= k; i++) {
B[i] = Q[i];
}
C = dot(QH1.Q, B);
for (i = 0; i <= k; i++) {
Q[i] = C[i];
}
B = Array(m - k - 1);
for (i = k + 1; i < m; i++) {
B[i - k - 1] = Q[i];
}
C = dot(QH2.Q, B);
for (i = k + 1; i < m; i++) {
Q[i] = C[i - k - 1];
}
return {
Q: Q,
B: QH1.B.concat(add(QH2.B, k + 1))
};
}
}
J = Math.min(m - 1, j + 3);
x = Array(J - j);
for (i = j + 1; i <= J; i++) {
x[i - j - 1] = H[i][j];
}
v = house(x);
B = getBlock(H, [j + 1, j], [J, m - 1]);
C = tensor(v, dot(v, B));
for (i = j + 1; i <= J; i++) {
Hi = H[i];
Ci = C[i - j - 1];
for (k = j; k < m; k++) Hi[k] -= 2 * Ci[k - j];
}
B = getBlock(H, [0, j + 1], [m - 1, J]);
C = tensor(dot(B, v), v);
for (i = 0; i < m; i++) {
Hi = H[i];
Ci = C[i];
for (k = j + 1; k <= J; k++) Hi[k] -= 2 * Ci[k - j - 1];
}
B = Array(J - j);
for (i = j + 1; i <= J; i++) B[i - j - 1] = Q[i];
C = tensor(v, dot(v, B));
for (i = j + 1; i <= J; i++) {
Qi = Q[i];
Ci = C[i - j - 1];
for (k = 0; k < m; k++) Qi[k] -= 2 * Ci[k];
}
}
}
throw new Error('mathlab: eigenvalue iteration does not converge -- increase maxiter?');
}