complexEigs.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.createComplexEigs = createComplexEigs;
  7. var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
  8. var _object = require("../../../utils/object.js");
  9. function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
  10. function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
  11. function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
  12. function createComplexEigs(_ref) {
  13. var addScalar = _ref.addScalar,
  14. subtract = _ref.subtract,
  15. flatten = _ref.flatten,
  16. multiply = _ref.multiply,
  17. multiplyScalar = _ref.multiplyScalar,
  18. divideScalar = _ref.divideScalar,
  19. sqrt = _ref.sqrt,
  20. abs = _ref.abs,
  21. bignumber = _ref.bignumber,
  22. diag = _ref.diag,
  23. inv = _ref.inv,
  24. qr = _ref.qr,
  25. usolve = _ref.usolve,
  26. usolveAll = _ref.usolveAll,
  27. equal = _ref.equal,
  28. complex = _ref.complex,
  29. larger = _ref.larger,
  30. smaller = _ref.smaller,
  31. matrixFromColumns = _ref.matrixFromColumns,
  32. dot = _ref.dot;
  33. /**
  34. * @param {number[][]} arr the matrix to find eigenvalues of
  35. * @param {number} N size of the matrix
  36. * @param {number|BigNumber} prec precision, anything lower will be considered zero
  37. * @param {'number'|'BigNumber'|'Complex'} type
  38. * @param {boolean} findVectors should we find eigenvectors?
  39. *
  40. * @returns {{ values: number[], vectors: number[][] }}
  41. */
  42. function complexEigs(arr, N, prec, type, findVectors) {
  43. if (findVectors === undefined) {
  44. findVectors = true;
  45. }
  46. // TODO check if any row/col are zero except the diagonal
  47. // make sure corresponding rows and columns have similar magnitude
  48. // important because of numerical stability
  49. // MODIFIES arr by side effect!
  50. var R = balance(arr, N, prec, type, findVectors);
  51. // R is the row transformation matrix
  52. // arr = A' = R A R⁻¹, A is the original matrix
  53. // (if findVectors is false, R is undefined)
  54. // (And so to return to original matrix: A = R⁻¹ arr R)
  55. // TODO if magnitudes of elements vary over many orders,
  56. // move greatest elements to the top left corner
  57. // using similarity transformations, reduce the matrix
  58. // to Hessenberg form (upper triangular plus one subdiagonal row)
  59. // updates the transformation matrix R with new row operationsq
  60. // MODIFIES arr by side effect!
  61. reduceToHessenberg(arr, N, prec, type, findVectors, R);
  62. // still true that original A = R⁻¹ arr R)
  63. // find eigenvalues
  64. var _iterateUntilTriangul = iterateUntilTriangular(arr, N, prec, type, findVectors),
  65. values = _iterateUntilTriangul.values,
  66. C = _iterateUntilTriangul.C;
  67. // values is the list of eigenvalues, C is the column
  68. // transformation matrix that transforms arr, the hessenberg
  69. // matrix, to upper triangular
  70. // (So U = C⁻¹ arr C and the relationship between current arr
  71. // and original A is unchanged.)
  72. var vectors;
  73. if (findVectors) {
  74. vectors = findEigenvectors(arr, N, C, R, values, prec, type);
  75. vectors = matrixFromColumns.apply(void 0, (0, _toConsumableArray2["default"])(vectors));
  76. }
  77. return {
  78. values: values,
  79. vectors: vectors
  80. };
  81. }
  82. /**
  83. * @param {number[][]} arr
  84. * @param {number} N
  85. * @param {number} prec
  86. * @param {'number'|'BigNumber'|'Complex'} type
  87. * @returns {number[][]}
  88. */
  89. function balance(arr, N, prec, type, findVectors) {
  90. var big = type === 'BigNumber';
  91. var cplx = type === 'Complex';
  92. var realzero = big ? bignumber(0) : 0;
  93. var one = big ? bignumber(1) : cplx ? complex(1) : 1;
  94. var realone = big ? bignumber(1) : 1;
  95. // base of the floating-point arithmetic
  96. var radix = big ? bignumber(10) : 2;
  97. var radixSq = multiplyScalar(radix, radix);
  98. // the diagonal transformation matrix R
  99. var Rdiag;
  100. if (findVectors) {
  101. Rdiag = Array(N).fill(one);
  102. }
  103. // this isn't the only time we loop thru the matrix...
  104. var last = false;
  105. while (!last) {
  106. // ...haha I'm joking! unless...
  107. last = true;
  108. for (var i = 0; i < N; i++) {
  109. // compute the taxicab norm of i-th column and row
  110. // TODO optimize for complex numbers
  111. var colNorm = realzero;
  112. var rowNorm = realzero;
  113. for (var j = 0; j < N; j++) {
  114. if (i === j) continue;
  115. var c = abs(arr[i][j]); // should be real
  116. colNorm = addScalar(colNorm, c);
  117. rowNorm = addScalar(rowNorm, c);
  118. }
  119. if (!equal(colNorm, 0) && !equal(rowNorm, 0)) {
  120. // find integer power closest to balancing the matrix
  121. // (we want to scale only by integer powers of radix,
  122. // so that we don't lose any precision due to round-off)
  123. var f = realone;
  124. var _c = colNorm;
  125. var rowDivRadix = divideScalar(rowNorm, radix);
  126. var rowMulRadix = multiplyScalar(rowNorm, radix);
  127. while (smaller(_c, rowDivRadix)) {
  128. _c = multiplyScalar(_c, radixSq);
  129. f = multiplyScalar(f, radix);
  130. }
  131. while (larger(_c, rowMulRadix)) {
  132. _c = divideScalar(_c, radixSq);
  133. f = divideScalar(f, radix);
  134. }
  135. // check whether balancing is needed
  136. // condition = (c + rowNorm) / f < 0.95 * (colNorm + rowNorm)
  137. var condition = smaller(divideScalar(addScalar(_c, rowNorm), f), multiplyScalar(addScalar(colNorm, rowNorm), 0.95));
  138. // apply balancing similarity transformation
  139. if (condition) {
  140. // we should loop once again to check whether
  141. // another rebalancing is needed
  142. last = false;
  143. var g = divideScalar(1, f);
  144. for (var _j = 0; _j < N; _j++) {
  145. if (i === _j) {
  146. continue;
  147. }
  148. arr[i][_j] = multiplyScalar(arr[i][_j], f);
  149. arr[_j][i] = multiplyScalar(arr[_j][i], g);
  150. }
  151. // keep track of transformations
  152. if (findVectors) {
  153. Rdiag[i] = multiplyScalar(Rdiag[i], f);
  154. }
  155. }
  156. }
  157. }
  158. }
  159. // return the diagonal row transformation matrix
  160. return diag(Rdiag);
  161. }
  162. /**
  163. * @param {number[][]} arr
  164. * @param {number} N
  165. * @param {number} prec
  166. * @param {'number'|'BigNumber'|'Complex'} type
  167. * @param {boolean} findVectors
  168. * @param {number[][]} R the row transformation matrix that will be modified
  169. */
  170. function reduceToHessenberg(arr, N, prec, type, findVectors, R) {
  171. var big = type === 'BigNumber';
  172. var cplx = type === 'Complex';
  173. var zero = big ? bignumber(0) : cplx ? complex(0) : 0;
  174. if (big) {
  175. prec = bignumber(prec);
  176. }
  177. for (var i = 0; i < N - 2; i++) {
  178. // Find the largest subdiag element in the i-th col
  179. var maxIndex = 0;
  180. var max = zero;
  181. for (var j = i + 1; j < N; j++) {
  182. var el = arr[j][i];
  183. if (smaller(abs(max), abs(el))) {
  184. max = el;
  185. maxIndex = j;
  186. }
  187. }
  188. // This col is pivoted, no need to do anything
  189. if (smaller(abs(max), prec)) {
  190. continue;
  191. }
  192. if (maxIndex !== i + 1) {
  193. // Interchange maxIndex-th and (i+1)-th row
  194. var tmp1 = arr[maxIndex];
  195. arr[maxIndex] = arr[i + 1];
  196. arr[i + 1] = tmp1;
  197. // Interchange maxIndex-th and (i+1)-th column
  198. for (var _j2 = 0; _j2 < N; _j2++) {
  199. var tmp2 = arr[_j2][maxIndex];
  200. arr[_j2][maxIndex] = arr[_j2][i + 1];
  201. arr[_j2][i + 1] = tmp2;
  202. }
  203. // keep track of transformations
  204. if (findVectors) {
  205. var tmp3 = R[maxIndex];
  206. R[maxIndex] = R[i + 1];
  207. R[i + 1] = tmp3;
  208. }
  209. }
  210. // Reduce following rows and columns
  211. for (var _j3 = i + 2; _j3 < N; _j3++) {
  212. var n = divideScalar(arr[_j3][i], max);
  213. if (n === 0) {
  214. continue;
  215. }
  216. // from j-th row subtract n-times (i+1)th row
  217. for (var k = 0; k < N; k++) {
  218. arr[_j3][k] = subtract(arr[_j3][k], multiplyScalar(n, arr[i + 1][k]));
  219. }
  220. // to (i+1)th column add n-times j-th column
  221. for (var _k = 0; _k < N; _k++) {
  222. arr[_k][i + 1] = addScalar(arr[_k][i + 1], multiplyScalar(n, arr[_k][_j3]));
  223. }
  224. // keep track of transformations
  225. if (findVectors) {
  226. for (var _k2 = 0; _k2 < N; _k2++) {
  227. R[_j3][_k2] = subtract(R[_j3][_k2], multiplyScalar(n, R[i + 1][_k2]));
  228. }
  229. }
  230. }
  231. }
  232. return R;
  233. }
  234. /**
  235. * @returns {{values: values, C: Matrix}}
  236. * @see Press, Wiliams: Numerical recipes in Fortran 77
  237. * @see https://en.wikipedia.org/wiki/QR_algorithm
  238. */
  239. function iterateUntilTriangular(A, N, prec, type, findVectors) {
  240. var big = type === 'BigNumber';
  241. var cplx = type === 'Complex';
  242. var one = big ? bignumber(1) : cplx ? complex(1) : 1;
  243. if (big) {
  244. prec = bignumber(prec);
  245. }
  246. // The Francis Algorithm
  247. // The core idea of this algorithm is that doing successive
  248. // A' = Q⁺AQ transformations will eventually converge to block-
  249. // upper-triangular with diagonal blocks either 1x1 or 2x2.
  250. // The Q here is the one from the QR decomposition, A = QR.
  251. // Since the eigenvalues of a block-upper-triangular matrix are
  252. // the eigenvalues of its diagonal blocks and we know how to find
  253. // eigenvalues of a 2x2 matrix, we know the eigenvalues of A.
  254. var arr = (0, _object.clone)(A);
  255. // the list of converged eigenvalues
  256. var lambdas = [];
  257. // size of arr, which will get smaller as eigenvalues converge
  258. var n = N;
  259. // the diagonal of the block-diagonal matrix that turns
  260. // converged 2x2 matrices into upper triangular matrices
  261. var Sdiag = [];
  262. // N×N matrix describing the overall transformation done during the QR algorithm
  263. var Qtotal = findVectors ? diag(Array(N).fill(one)) : undefined;
  264. // n×n matrix describing the QR transformations done since last convergence
  265. var Qpartial = findVectors ? diag(Array(n).fill(one)) : undefined;
  266. // last eigenvalue converged before this many steps
  267. var lastConvergenceBefore = 0;
  268. while (lastConvergenceBefore <= 100) {
  269. lastConvergenceBefore += 1;
  270. // TODO if the convergence is slow, do something clever
  271. // Perform the factorization
  272. var k = 0; // TODO set close to an eigenvalue
  273. for (var i = 0; i < n; i++) {
  274. arr[i][i] = subtract(arr[i][i], k);
  275. }
  276. // TODO do an implicit QR transformation
  277. var _qr = qr(arr),
  278. Q = _qr.Q,
  279. R = _qr.R;
  280. arr = multiply(R, Q);
  281. for (var _i = 0; _i < n; _i++) {
  282. arr[_i][_i] = addScalar(arr[_i][_i], k);
  283. }
  284. // keep track of transformations
  285. if (findVectors) {
  286. Qpartial = multiply(Qpartial, Q);
  287. }
  288. // The rightmost diagonal element converged to an eigenvalue
  289. if (n === 1 || smaller(abs(arr[n - 1][n - 2]), prec)) {
  290. lastConvergenceBefore = 0;
  291. lambdas.push(arr[n - 1][n - 1]);
  292. // keep track of transformations
  293. if (findVectors) {
  294. Sdiag.unshift([[1]]);
  295. inflateMatrix(Qpartial, N);
  296. Qtotal = multiply(Qtotal, Qpartial);
  297. if (n > 1) {
  298. Qpartial = diag(Array(n - 1).fill(one));
  299. }
  300. }
  301. // reduce the matrix size
  302. n -= 1;
  303. arr.pop();
  304. for (var _i2 = 0; _i2 < n; _i2++) {
  305. arr[_i2].pop();
  306. }
  307. // The rightmost diagonal 2x2 block converged
  308. } else if (n === 2 || smaller(abs(arr[n - 2][n - 3]), prec)) {
  309. lastConvergenceBefore = 0;
  310. var ll = eigenvalues2x2(arr[n - 2][n - 2], arr[n - 2][n - 1], arr[n - 1][n - 2], arr[n - 1][n - 1]);
  311. lambdas.push.apply(lambdas, (0, _toConsumableArray2["default"])(ll));
  312. // keep track of transformations
  313. if (findVectors) {
  314. Sdiag.unshift(jordanBase2x2(arr[n - 2][n - 2], arr[n - 2][n - 1], arr[n - 1][n - 2], arr[n - 1][n - 1], ll[0], ll[1], prec, type));
  315. inflateMatrix(Qpartial, N);
  316. Qtotal = multiply(Qtotal, Qpartial);
  317. if (n > 2) {
  318. Qpartial = diag(Array(n - 2).fill(one));
  319. }
  320. }
  321. // reduce the matrix size
  322. n -= 2;
  323. arr.pop();
  324. arr.pop();
  325. for (var _i3 = 0; _i3 < n; _i3++) {
  326. arr[_i3].pop();
  327. arr[_i3].pop();
  328. }
  329. }
  330. if (n === 0) {
  331. break;
  332. }
  333. }
  334. // standard sorting
  335. lambdas.sort(function (a, b) {
  336. return +subtract(abs(a), abs(b));
  337. });
  338. // the algorithm didn't converge
  339. if (lastConvergenceBefore > 100) {
  340. var err = Error('The eigenvalues failed to converge. Only found these eigenvalues: ' + lambdas.join(', '));
  341. err.values = lambdas;
  342. err.vectors = [];
  343. throw err;
  344. }
  345. // combine the overall QR transformation Qtotal with the subsequent
  346. // transformation S that turns the diagonal 2x2 blocks to upper triangular
  347. var C = findVectors ? multiply(Qtotal, blockDiag(Sdiag, N)) : undefined;
  348. return {
  349. values: lambdas,
  350. C: C
  351. };
  352. }
  353. /**
  354. * @param {Matrix} A hessenberg-form matrix
  355. * @param {number} N size of A
  356. * @param {Matrix} C column transformation matrix that turns A into upper triangular
  357. * @param {Matrix} R similarity that turns original matrix into A
  358. * @param {number[]} values array of eigenvalues of A
  359. * @param {'number'|'BigNumber'|'Complex'} type
  360. * @returns {number[][]} eigenvalues
  361. */
  362. function findEigenvectors(A, N, C, R, values, prec, type) {
  363. var Cinv = inv(C);
  364. var U = multiply(Cinv, A, C);
  365. var big = type === 'BigNumber';
  366. var cplx = type === 'Complex';
  367. var zero = big ? bignumber(0) : cplx ? complex(0) : 0;
  368. var one = big ? bignumber(1) : cplx ? complex(1) : 1;
  369. // turn values into a kind of "multiset"
  370. // this way it is easier to find eigenvectors
  371. var uniqueValues = [];
  372. var multiplicities = [];
  373. var _iterator = _createForOfIteratorHelper(values),
  374. _step;
  375. try {
  376. for (_iterator.s(); !(_step = _iterator.n()).done;) {
  377. var λ = _step.value;
  378. var _i4 = indexOf(uniqueValues, λ, equal);
  379. if (_i4 === -1) {
  380. uniqueValues.push(λ);
  381. multiplicities.push(1);
  382. } else {
  383. multiplicities[_i4] += 1;
  384. }
  385. }
  386. // find eigenvectors by solving U − λE = 0
  387. // TODO replace with an iterative eigenvector algorithm
  388. // (this one might fail for imprecise eigenvalues)
  389. } catch (err) {
  390. _iterator.e(err);
  391. } finally {
  392. _iterator.f();
  393. }
  394. var vectors = [];
  395. var len = uniqueValues.length;
  396. var b = Array(N).fill(zero);
  397. var E = diag(Array(N).fill(one));
  398. // eigenvalues for which usolve failed (due to numerical error)
  399. var failedLambdas = [];
  400. var _loop = function _loop(i) {
  401. var λ = uniqueValues[i];
  402. var S = subtract(U, multiply(λ, E)); // the characteristic matrix
  403. var solutions = usolveAll(S, b);
  404. solutions.shift(); // ignore the null vector
  405. // looks like we missed something, try inverse iteration
  406. while (solutions.length < multiplicities[i]) {
  407. var approxVec = inverseIterate(S, N, solutions, prec, type);
  408. if (approxVec == null) {
  409. // no more vectors were found
  410. failedLambdas.push(λ);
  411. break;
  412. }
  413. solutions.push(approxVec);
  414. }
  415. // Transform back into original array coordinates
  416. var correction = multiply(inv(R), C);
  417. solutions = solutions.map(function (v) {
  418. return multiply(correction, v);
  419. });
  420. vectors.push.apply(vectors, (0, _toConsumableArray2["default"])(solutions.map(function (v) {
  421. return flatten(v);
  422. })));
  423. };
  424. for (var i = 0; i < len; i++) {
  425. _loop(i);
  426. }
  427. if (failedLambdas.length !== 0) {
  428. var err = new Error('Failed to find eigenvectors for the following eigenvalues: ' + failedLambdas.join(', '));
  429. err.values = values;
  430. err.vectors = vectors;
  431. throw err;
  432. }
  433. return vectors;
  434. }
  435. /**
  436. * Compute the eigenvalues of an 2x2 matrix
  437. * @return {[number,number]}
  438. */
  439. function eigenvalues2x2(a, b, c, d) {
  440. // λ± = ½ trA ± ½ √( tr²A - 4 detA )
  441. var trA = addScalar(a, d);
  442. var detA = subtract(multiplyScalar(a, d), multiplyScalar(b, c));
  443. var x = multiplyScalar(trA, 0.5);
  444. var y = multiplyScalar(sqrt(subtract(multiplyScalar(trA, trA), multiplyScalar(4, detA))), 0.5);
  445. return [addScalar(x, y), subtract(x, y)];
  446. }
  447. /**
  448. * For an 2x2 matrix compute the transformation matrix S,
  449. * so that SAS⁻¹ is an upper triangular matrix
  450. * @return {[[number,number],[number,number]]}
  451. * @see https://math.berkeley.edu/~ogus/old/Math_54-05/webfoils/jordan.pdf
  452. * @see http://people.math.harvard.edu/~knill/teaching/math21b2004/exhibits/2dmatrices/index.html
  453. */
  454. function jordanBase2x2(a, b, c, d, l1, l2, prec, type) {
  455. var big = type === 'BigNumber';
  456. var cplx = type === 'Complex';
  457. var zero = big ? bignumber(0) : cplx ? complex(0) : 0;
  458. var one = big ? bignumber(1) : cplx ? complex(1) : 1;
  459. // matrix is already upper triangular
  460. // return an identity matrix
  461. if (smaller(abs(c), prec)) {
  462. return [[one, zero], [zero, one]];
  463. }
  464. // matrix is diagonalizable
  465. // return its eigenvectors as columns
  466. if (larger(abs(subtract(l1, l2)), prec)) {
  467. return [[subtract(l1, d), subtract(l2, d)], [c, c]];
  468. }
  469. // matrix is not diagonalizable
  470. // compute off-diagonal elements of N = A - λI
  471. // N₁₂ = 0 ⇒ S = ( N⃗₁, I⃗₁ )
  472. // N₁₂ ≠ 0 ⇒ S = ( N⃗₂, I⃗₂ )
  473. var na = subtract(a, l1);
  474. var nb = subtract(b, l1);
  475. var nc = subtract(c, l1);
  476. var nd = subtract(d, l1);
  477. if (smaller(abs(nb), prec)) {
  478. return [[na, one], [nc, zero]];
  479. } else {
  480. return [[nb, zero], [nd, one]];
  481. }
  482. }
  483. /**
  484. * Enlarge the matrix from n×n to N×N, setting the new
  485. * elements to 1 on diagonal and 0 elsewhere
  486. */
  487. function inflateMatrix(arr, N) {
  488. // add columns
  489. for (var i = 0; i < arr.length; i++) {
  490. var _arr$i;
  491. (_arr$i = arr[i]).push.apply(_arr$i, (0, _toConsumableArray2["default"])(Array(N - arr[i].length).fill(0)));
  492. }
  493. // add rows
  494. for (var _i5 = arr.length; _i5 < N; _i5++) {
  495. arr.push(Array(N).fill(0));
  496. arr[_i5][_i5] = 1;
  497. }
  498. return arr;
  499. }
  500. /**
  501. * Create a block-diagonal matrix with the given square matrices on the diagonal
  502. * @param {Matrix[] | number[][][]} arr array of matrices to be placed on the diagonal
  503. * @param {number} N the size of the resulting matrix
  504. */
  505. function blockDiag(arr, N) {
  506. var M = [];
  507. for (var i = 0; i < N; i++) {
  508. M[i] = Array(N).fill(0);
  509. }
  510. var I = 0;
  511. var _iterator2 = _createForOfIteratorHelper(arr),
  512. _step2;
  513. try {
  514. for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
  515. var sub = _step2.value;
  516. var n = sub.length;
  517. for (var _i6 = 0; _i6 < n; _i6++) {
  518. for (var j = 0; j < n; j++) {
  519. M[I + _i6][I + j] = sub[_i6][j];
  520. }
  521. }
  522. I += n;
  523. }
  524. } catch (err) {
  525. _iterator2.e(err);
  526. } finally {
  527. _iterator2.f();
  528. }
  529. return M;
  530. }
  531. /**
  532. * Finds the index of an element in an array using a custom equality function
  533. * @template T
  534. * @param {Array<T>} arr array in which to search
  535. * @param {T} el the element to find
  536. * @param {function(T, T): boolean} fn the equality function, first argument is an element of `arr`, the second is always `el`
  537. * @returns {number} the index of `el`, or -1 when it's not in `arr`
  538. */
  539. function indexOf(arr, el, fn) {
  540. for (var i = 0; i < arr.length; i++) {
  541. if (fn(arr[i], el)) {
  542. return i;
  543. }
  544. }
  545. return -1;
  546. }
  547. /**
  548. * Provided a near-singular upper-triangular matrix A and a list of vectors,
  549. * finds an eigenvector of A with the smallest eigenvalue, which is orthogonal
  550. * to each vector in the list
  551. * @template T
  552. * @param {T[][]} A near-singular square matrix
  553. * @param {number} N dimension
  554. * @param {T[][]} orthog list of vectors
  555. * @param {number} prec epsilon
  556. * @param {'number'|'BigNumber'|'Complex'} type
  557. * @return {T[] | null} eigenvector
  558. *
  559. * @see Numerical Recipes for Fortran 77 – 11.7 Eigenvalues or Eigenvectors by Inverse Iteration
  560. */
  561. function inverseIterate(A, N, orthog, prec, type) {
  562. var largeNum = type === 'BigNumber' ? bignumber(1000) : 1000;
  563. var b; // the vector
  564. // you better choose a random vector before I count to five
  565. var i = 0;
  566. while (true) {
  567. b = randomOrthogonalVector(N, orthog, type);
  568. b = usolve(A, b);
  569. if (larger(norm(b), largeNum)) {
  570. break;
  571. }
  572. if (++i >= 5) {
  573. return null;
  574. }
  575. }
  576. // you better converge before I count to ten
  577. i = 0;
  578. while (true) {
  579. var c = usolve(A, b);
  580. if (smaller(norm(orthogonalComplement(b, [c])), prec)) {
  581. break;
  582. }
  583. if (++i >= 10) {
  584. return null;
  585. }
  586. b = normalize(c);
  587. }
  588. return b;
  589. }
  590. /**
  591. * Generates a random unit vector of dimension N, orthogonal to each vector in the list
  592. * @template T
  593. * @param {number} N dimension
  594. * @param {T[][]} orthog list of vectors
  595. * @param {'number'|'BigNumber'|'Complex'} type
  596. * @returns {T[]} random vector
  597. */
  598. function randomOrthogonalVector(N, orthog, type) {
  599. var big = type === 'BigNumber';
  600. var cplx = type === 'Complex';
  601. // generate random vector with the correct type
  602. var v = Array(N).fill(0).map(function (_) {
  603. return 2 * Math.random() - 1;
  604. });
  605. if (big) {
  606. v = v.map(function (n) {
  607. return bignumber(n);
  608. });
  609. }
  610. if (cplx) {
  611. v = v.map(function (n) {
  612. return complex(n);
  613. });
  614. }
  615. // project to orthogonal complement
  616. v = orthogonalComplement(v, orthog);
  617. // normalize
  618. return normalize(v, type);
  619. }
  620. /**
  621. * Project vector v to the orthogonal complement of an array of vectors
  622. */
  623. function orthogonalComplement(v, orthog) {
  624. var _iterator3 = _createForOfIteratorHelper(orthog),
  625. _step3;
  626. try {
  627. for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
  628. var w = _step3.value;
  629. // v := v − (w, v)/∥w∥² w
  630. v = subtract(v, multiply(divideScalar(dot(w, v), dot(w, w)), w));
  631. }
  632. } catch (err) {
  633. _iterator3.e(err);
  634. } finally {
  635. _iterator3.f();
  636. }
  637. return v;
  638. }
  639. /**
  640. * Calculate the norm of a vector.
  641. * We can't use math.norm because factory can't handle circular dependency.
  642. * Seriously, I'm really fed up with factory.
  643. */
  644. function norm(v) {
  645. return abs(sqrt(dot(v, v)));
  646. }
  647. /**
  648. * Normalize a vector
  649. * @template T
  650. * @param {T[]} v
  651. * @param {'number'|'BigNumber'|'Complex'} type
  652. * @returns {T[]} normalized vec
  653. */
  654. function normalize(v, type) {
  655. var big = type === 'BigNumber';
  656. var cplx = type === 'Complex';
  657. var one = big ? bignumber(1) : cplx ? complex(1) : 1;
  658. return multiply(divideScalar(one, norm(v)), v);
  659. }
  660. return complexEigs;
  661. }