jiangchengfeiyi-xiaochengxu/node_modules/mathjs/lib/cjs/function/matrix/eigs.js
2025-01-02 11:13:50 +08:00

335 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createEigs = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _factory = require("../../utils/factory.js");
var _string = require("../../utils/string.js");
var _complexEigs = require("./eigs/complexEigs.js");
var _realSymmetric = require("./eigs/realSymmetric.js");
var _is = require("../../utils/is.js");
const name = 'eigs';
// The absolute state of math.js's dependency system:
const dependencies = ['config', 'typed', 'matrix', 'addScalar', 'equal', 'subtract', 'abs', 'atan', 'cos', 'sin', 'multiplyScalar', 'divideScalar', 'inv', 'bignumber', 'multiply', 'add', 'larger', 'column', 'flatten', 'number', 'complex', 'sqrt', 'diag', 'size', 'reshape', 'qr', 'usolve', 'usolveAll', 'im', 're', 'smaller', 'matrixFromColumns', 'dot'];
const createEigs = exports.createEigs = /* #__PURE__ */(0, _factory.factory)(name, dependencies, _ref => {
let {
config,
typed,
matrix,
addScalar,
subtract,
equal,
abs,
atan,
cos,
sin,
multiplyScalar,
divideScalar,
inv,
bignumber,
multiply,
add,
larger,
column,
flatten,
number,
complex,
sqrt,
diag,
size,
reshape,
qr,
usolve,
usolveAll,
im,
re,
smaller,
matrixFromColumns,
dot
} = _ref;
const doRealSymmetric = (0, _realSymmetric.createRealSymmetric)({
config,
addScalar,
subtract,
column,
flatten,
equal,
abs,
atan,
cos,
sin,
multiplyScalar,
inv,
bignumber,
complex,
multiply,
add
});
const doComplexEigs = (0, _complexEigs.createComplexEigs)({
config,
addScalar,
subtract,
multiply,
multiplyScalar,
flatten,
divideScalar,
sqrt,
abs,
bignumber,
diag,
size,
reshape,
qr,
inv,
usolve,
usolveAll,
equal,
complex,
larger,
smaller,
matrixFromColumns,
dot
});
/**
* Compute eigenvalues and optionally eigenvectors of a square matrix.
* The eigenvalues are sorted by their absolute value, ascending, and
* returned as a vector in the `values` property of the returned project.
* An eigenvalue with algebraic multiplicity k will be listed k times, so
* that the returned `values` vector always has length equal to the size
* of the input matrix.
*
* The `eigenvectors` property of the return value provides the eigenvectors.
* It is an array of plain objects: the `value` property of each gives the
* associated eigenvalue, and the `vector` property gives the eigenvector
* itself. Note that the same `value` property will occur as many times in
* the list provided by `eigenvectors` as the geometric multiplicity of
* that value.
*
* If the algorithm fails to converge, it will throw an error
* in that case, however, you may still find useful information
* in `err.values` and `err.vectors`.
*
* Note that the 'precision' option does not directly specify the _accuracy_
* of the returned eigenvalues. Rather, it determines how small an entry
* of the iterative approximations to an upper triangular matrix must be
* in order to be considered zero. The actual accuracy of the returned
* eigenvalues may be greater or less than the precision, depending on the
* conditioning of the matrix and how far apart or close the actual
* eigenvalues are. Note that currently, relatively simple, "traditional"
* methods of eigenvalue computation are being used; this is not a modern,
* high-precision eigenvalue computation. That said, it should typically
* produce fairly reasonable results.
*
* Syntax:
*
* math.eigs(x, [prec])
* math.eigs(x, {options})
*
* Examples:
*
* const { eigs, multiply, column, transpose, matrixFromColumns } = math
* const H = [[5, 2.3], [2.3, 1]]
* const ans = eigs(H) // returns {values: [E1,E2...sorted], eigenvectors: [{value: E1, vector: v2}, {value: e, vector: v2}, ...]
* const E = ans.values
* const V = ans.eigenvectors
* multiply(H, V[0].vector)) // returns multiply(E[0], V[0].vector))
* const U = matrixFromColumns(...V.map(obj => obj.vector))
* const UTxHxU = multiply(transpose(U), H, U) // diagonalizes H if possible
* E[0] == UTxHxU[0][0] // returns true always
*
* // Compute only approximate eigenvalues:
* const {values} = eigs(H, {eigenvectors: false, precision: 1e-6})
*
* See also:
*
* inv
*
* @param {Array | Matrix} x Matrix to be diagonalized
*
* @param {number | BigNumber | OptsObject} [opts] Object with keys `precision`, defaulting to config.relTol, and `eigenvectors`, defaulting to true and specifying whether to compute eigenvectors. If just a number, specifies precision.
* @return {{values: Array|Matrix, eigenvectors?: Array<EVobj>}} Object containing an array of eigenvalues and an array of {value: number|BigNumber, vector: Array|Matrix} objects. The eigenvectors property is undefined if eigenvectors were not requested.
*
*/
return typed('eigs', {
// The conversion to matrix in the first two implementations,
// just to convert back to an array right away in
// computeValuesAndVectors, is unfortunate, and should perhaps be
// streamlined. It is done because the Matrix object carries some
// type information about its entries, and so constructing the matrix
// is a roundabout way of doing type detection.
Array: function (x) {
return doEigs(matrix(x));
},
'Array, number|BigNumber': function (x, prec) {
return doEigs(matrix(x), {
precision: prec
});
},
'Array, Object'(x, opts) {
return doEigs(matrix(x), opts);
},
Matrix: function (mat) {
return doEigs(mat, {
matricize: true
});
},
'Matrix, number|BigNumber': function (mat, prec) {
return doEigs(mat, {
precision: prec,
matricize: true
});
},
'Matrix, Object': function (mat, opts) {
const useOpts = {
matricize: true
};
(0, _extends2.default)(useOpts, opts);
return doEigs(mat, useOpts);
}
});
function doEigs(mat) {
var _opts$precision;
let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
const computeVectors = 'eigenvectors' in opts ? opts.eigenvectors : true;
const prec = (_opts$precision = opts.precision) !== null && _opts$precision !== void 0 ? _opts$precision : config.relTol;
const result = computeValuesAndVectors(mat, prec, computeVectors);
if (opts.matricize) {
result.values = matrix(result.values);
if (computeVectors) {
result.eigenvectors = result.eigenvectors.map(_ref2 => {
let {
value,
vector
} = _ref2;
return {
value,
vector: matrix(vector)
};
});
}
}
if (computeVectors) {
Object.defineProperty(result, 'vectors', {
enumerable: false,
// to make sure that the eigenvectors can still be
// converted to string.
get: () => {
throw new Error('eigs(M).vectors replaced with eigs(M).eigenvectors');
}
});
}
return result;
}
function computeValuesAndVectors(mat, prec, computeVectors) {
const arr = mat.toArray(); // NOTE: arr is guaranteed to be unaliased
// and so safe to modify in place
const asize = mat.size();
if (asize.length !== 2 || asize[0] !== asize[1]) {
throw new RangeError(`Matrix must be square (size: ${(0, _string.format)(asize)})`);
}
const N = asize[0];
if (isReal(arr, N, prec)) {
coerceReal(arr, N); // modifies arr by side effect
if (isSymmetric(arr, N, prec)) {
const type = coerceTypes(mat, arr, N); // modifies arr by side effect
return doRealSymmetric(arr, N, prec, type, computeVectors);
}
}
const type = coerceTypes(mat, arr, N); // modifies arr by side effect
return doComplexEigs(arr, N, prec, type, computeVectors);
}
/** @return {boolean} */
function isSymmetric(arr, N, prec) {
for (let i = 0; i < N; i++) {
for (let j = i; j < N; j++) {
// TODO proper comparison of bignum and frac
if (larger(bignumber(abs(subtract(arr[i][j], arr[j][i]))), prec)) {
return false;
}
}
}
return true;
}
/** @return {boolean} */
function isReal(arr, N, prec) {
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
// TODO proper comparison of bignum and frac
if (larger(bignumber(abs(im(arr[i][j]))), prec)) {
return false;
}
}
}
return true;
}
function coerceReal(arr, N) {
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
arr[i][j] = re(arr[i][j]);
}
}
}
/** @return {'number' | 'BigNumber' | 'Complex'} */
function coerceTypes(mat, arr, N) {
/** @type {string} */
const type = mat.datatype();
if (type === 'number' || type === 'BigNumber' || type === 'Complex') {
return type;
}
let hasNumber = false;
let hasBig = false;
let hasComplex = false;
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
const el = arr[i][j];
if ((0, _is.isNumber)(el) || (0, _is.isFraction)(el)) {
hasNumber = true;
} else if ((0, _is.isBigNumber)(el)) {
hasBig = true;
} else if ((0, _is.isComplex)(el)) {
hasComplex = true;
} else {
throw TypeError('Unsupported type in Matrix: ' + (0, _is.typeOf)(el));
}
}
}
if (hasBig && hasComplex) {
console.warn('Complex BigNumbers not supported, this operation will lose precission.');
}
if (hasComplex) {
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
arr[i][j] = complex(arr[i][j]);
}
}
return 'Complex';
}
if (hasBig) {
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
arr[i][j] = bignumber(arr[i][j]);
}
}
return 'BigNumber';
}
if (hasNumber) {
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
arr[i][j] = number(arr[i][j]);
}
}
return 'number';
} else {
throw TypeError('Matrix contains unsupported types only.');
}
}
});