ml-matrix 6.12.2 → 6.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/matrix.d.ts +22 -2
- package/matrix.js +192 -73
- package/matrix.js.map +1 -1
- package/matrix.umd.js +1 -1
- package/matrix.umd.js.map +1 -1
- package/package.json +2 -1
- package/src/dc/evd.js +35 -24
- package/src/dc/svd.js +66 -51
- package/src/dc/util.js +21 -0
- package/src/matrix.js +72 -0
package/matrix.d.ts
CHANGED
|
@@ -616,6 +616,26 @@ export abstract class AbstractMatrix {
|
|
|
616
616
|
*/
|
|
617
617
|
mmul(other: MaybeMatrix): Matrix;
|
|
618
618
|
|
|
619
|
+
/**
|
|
620
|
+
* Returns the Gram matrix `thisᵀ · this` (the `columns × columns` matrix of
|
|
621
|
+
* the dot products of every pair of columns). The result is symmetric, so only
|
|
622
|
+
* its upper triangle is computed and mirrored, and the transpose is never
|
|
623
|
+
* materialized, making it about twice as fast as `this.transpose().mmul(this)`
|
|
624
|
+
* on dense matrices and much faster on sparse ones (zero entries are skipped).
|
|
625
|
+
* For finite inputs the result is identical to `this.transpose().mmul(this)`.
|
|
626
|
+
*/
|
|
627
|
+
gram(): Matrix;
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Returns the matrix product between `this` and its transpose (`this · thisᵀ`),
|
|
631
|
+
* optionally weighting each column by `scale` (`this · diag(scale) · thisᵀ`).
|
|
632
|
+
* The result is symmetric, so only its upper triangle is computed and mirrored
|
|
633
|
+
* and the transpose is never materialized, making it about twice as fast as
|
|
634
|
+
* `this.mmul(this.transpose())`.
|
|
635
|
+
* @param scale - Optional per-column factors (length equal to the number of columns).
|
|
636
|
+
*/
|
|
637
|
+
mmulByTranspose(scale?: ArrayLike<number>): Matrix;
|
|
638
|
+
|
|
619
639
|
/**
|
|
620
640
|
* Returns the square matrix raised to the given power
|
|
621
641
|
* @param scalar - the non-negative integer power to raise this matrix to
|
|
@@ -860,7 +880,7 @@ export abstract class AbstractMatrix {
|
|
|
860
880
|
[row: number, column: number, value: number],
|
|
861
881
|
void,
|
|
862
882
|
void
|
|
863
|
-
|
|
883
|
+
>;
|
|
864
884
|
|
|
865
885
|
/**
|
|
866
886
|
* iterator from left to right, from top to bottom
|
|
@@ -1489,7 +1509,7 @@ export { EigenvalueDecomposition as EVD };
|
|
|
1489
1509
|
* The Cholesky decomposition of a matrix. Given a matrix A, the Cholesky Decomposition of A is L
|
|
1490
1510
|
* such that A = (L)(L.transpose).
|
|
1491
1511
|
* Only works for symmetric, positive definite matrix A.
|
|
1492
|
-
*
|
|
1512
|
+
*
|
|
1493
1513
|
* @link https://github.com/lutzroeder/Mapack/blob/master/Source/CholeskyDecomposition.cs
|
|
1494
1514
|
*/
|
|
1495
1515
|
export class CholeskyDecomposition {
|
package/matrix.js
CHANGED
|
@@ -2216,6 +2216,78 @@ class AbstractMatrix {
|
|
|
2216
2216
|
return result;
|
|
2217
2217
|
}
|
|
2218
2218
|
|
|
2219
|
+
gram() {
|
|
2220
|
+
const rows = this.rows;
|
|
2221
|
+
const n = this.columns;
|
|
2222
|
+
|
|
2223
|
+
// The Gram matrix `thisᵀ · this` is symmetric, so only its upper triangle is
|
|
2224
|
+
// accumulated (then mirrored) and the transpose is never materialized.
|
|
2225
|
+
// Row-streaming rank-1 updates read each row of `this` contiguously and skip
|
|
2226
|
+
// zero entries, so the cost scales with the number of non-zeros: it is as
|
|
2227
|
+
// fast as the dense version on dense matrices (the skip never fires) and far
|
|
2228
|
+
// faster on sparse ones.
|
|
2229
|
+
const gramData = new Float64Array(n * n);
|
|
2230
|
+
for (let r = 0; r < rows; r++) {
|
|
2231
|
+
for (let i = 0; i < n; i++) {
|
|
2232
|
+
const value = this.get(r, i);
|
|
2233
|
+
if (value === 0) continue;
|
|
2234
|
+
const offset = i * n;
|
|
2235
|
+
for (let j = i; j < n; j++) {
|
|
2236
|
+
gramData[offset + j] += value * this.get(r, j);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
const result = new Matrix(n, n);
|
|
2242
|
+
for (let i = 0; i < n; i++) {
|
|
2243
|
+
const offset = i * n;
|
|
2244
|
+
for (let j = i; j < n; j++) {
|
|
2245
|
+
const value = gramData[offset + j];
|
|
2246
|
+
result.set(i, j, value);
|
|
2247
|
+
result.set(j, i, value);
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
return result;
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
mmulByTranspose(scale) {
|
|
2254
|
+
let m = this.rows;
|
|
2255
|
+
let n = this.columns;
|
|
2256
|
+
|
|
2257
|
+
if (scale !== undefined && scale.length !== n) {
|
|
2258
|
+
throw new RangeError('scale must have one value per column');
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
let result = new Matrix(m, m);
|
|
2262
|
+
|
|
2263
|
+
// result = this · diag(scale) · thisᵀ is symmetric, so only the upper
|
|
2264
|
+
// triangle is computed and mirrored, and the transpose is never
|
|
2265
|
+
// materialized. `scale` (one factor per column) is folded into one operand.
|
|
2266
|
+
let rowj = new Float64Array(n);
|
|
2267
|
+
for (let j = 0; j < m; j++) {
|
|
2268
|
+
if (scale === undefined) {
|
|
2269
|
+
for (let k = 0; k < n; k++) {
|
|
2270
|
+
rowj[k] = this.get(j, k);
|
|
2271
|
+
}
|
|
2272
|
+
} else {
|
|
2273
|
+
for (let k = 0; k < n; k++) {
|
|
2274
|
+
rowj[k] = scale[k] * this.get(j, k);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
for (let i = j; i < m; i++) {
|
|
2279
|
+
let s = 0;
|
|
2280
|
+
for (let k = 0; k < n; k++) {
|
|
2281
|
+
s += this.get(i, k) * rowj[k];
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
result.set(i, j, s);
|
|
2285
|
+
result.set(j, i, s);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
return result;
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2219
2291
|
mpow(scalar) {
|
|
2220
2292
|
if (!this.isSquare()) {
|
|
2221
2293
|
throw new RangeError('Matrix must be square');
|
|
@@ -3851,6 +3923,27 @@ class LuDecomposition {
|
|
|
3851
3923
|
}
|
|
3852
3924
|
}
|
|
3853
3925
|
|
|
3926
|
+
/**
|
|
3927
|
+
* Transpose a square matrix in place, without allocating a copy.
|
|
3928
|
+
* Used to restore the logical layout of decomposition outputs that were
|
|
3929
|
+
* accumulated in transposed storage for cache-sequential inner loops.
|
|
3930
|
+
* @param {import('../matrix').default} matrix - square matrix, mutated in place
|
|
3931
|
+
* @returns {import('../matrix').default} the same matrix
|
|
3932
|
+
*/
|
|
3933
|
+
function transposeSquareInPlace(matrix) {
|
|
3934
|
+
const data = matrix.data;
|
|
3935
|
+
const n = matrix.rows;
|
|
3936
|
+
for (let i = 0; i < n; i++) {
|
|
3937
|
+
const rowI = data[i];
|
|
3938
|
+
for (let j = i + 1; j < n; j++) {
|
|
3939
|
+
const tmp = rowI[j];
|
|
3940
|
+
rowI[j] = data[j][i];
|
|
3941
|
+
data[j][i] = tmp;
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
return matrix;
|
|
3945
|
+
}
|
|
3946
|
+
|
|
3854
3947
|
function hypotenuse(a, b) {
|
|
3855
3948
|
let r = 0;
|
|
3856
3949
|
if (Math.abs(a) > Math.abs(b)) {
|
|
@@ -4029,32 +4122,40 @@ class SingularValueDecomposition {
|
|
|
4029
4122
|
let wantu = Boolean(computeLeftSingularVectors);
|
|
4030
4123
|
let wantv = Boolean(computeRightSingularVectors);
|
|
4031
4124
|
|
|
4125
|
+
// Work on the transpose of the input so the hot inner loops (which iterate
|
|
4126
|
+
// over rows for a fixed column) scan memory sequentially in the row-major
|
|
4127
|
+
// backing store. `at` holds the transpose: at.get(j, i) === a.get(i, j)
|
|
4128
|
+
// where `a` is the logical m x n working matrix.
|
|
4032
4129
|
let swapped = false;
|
|
4033
|
-
let
|
|
4130
|
+
let at;
|
|
4034
4131
|
if (m < n) {
|
|
4035
4132
|
if (!autoTranspose) {
|
|
4036
|
-
a = value.clone();
|
|
4037
4133
|
// eslint-disable-next-line no-console
|
|
4038
4134
|
console.warn(
|
|
4039
4135
|
'Computing SVD on a matrix with more columns than rows. Consider enabling autoTranspose',
|
|
4040
4136
|
);
|
|
4137
|
+
at = value.transpose();
|
|
4041
4138
|
} else {
|
|
4042
|
-
|
|
4043
|
-
m =
|
|
4044
|
-
n =
|
|
4139
|
+
at = value.clone();
|
|
4140
|
+
m = value.columns;
|
|
4141
|
+
n = value.rows;
|
|
4045
4142
|
swapped = true;
|
|
4046
4143
|
let aux = wantu;
|
|
4047
4144
|
wantu = wantv;
|
|
4048
4145
|
wantv = aux;
|
|
4049
4146
|
}
|
|
4050
4147
|
} else {
|
|
4051
|
-
|
|
4148
|
+
at = value.transpose();
|
|
4052
4149
|
}
|
|
4053
4150
|
|
|
4054
4151
|
let nu = Math.min(m, n);
|
|
4055
4152
|
let ni = Math.min(m + 1, n);
|
|
4056
4153
|
let s = new Float64Array(ni);
|
|
4057
|
-
|
|
4154
|
+
// U and V are stored transposed during the computation so the inner loops
|
|
4155
|
+
// (which always vary the row index) scan memory sequentially. They are
|
|
4156
|
+
// transposed back to their logical layout before being returned.
|
|
4157
|
+
// Ut.get(j, i) === U.get(i, j) and Vt.get(j, i) === V.get(i, j).
|
|
4158
|
+
let U = new Matrix(nu, m);
|
|
4058
4159
|
let V = new Matrix(n, n);
|
|
4059
4160
|
|
|
4060
4161
|
let e = new Float64Array(n);
|
|
@@ -4071,16 +4172,16 @@ class SingularValueDecomposition {
|
|
|
4071
4172
|
if (k < nct) {
|
|
4072
4173
|
s[k] = 0;
|
|
4073
4174
|
for (let i = k; i < m; i++) {
|
|
4074
|
-
s[k] = hypotenuse(s[k],
|
|
4175
|
+
s[k] = hypotenuse(s[k], at.get(k, i));
|
|
4075
4176
|
}
|
|
4076
4177
|
if (s[k] !== 0) {
|
|
4077
|
-
if (
|
|
4178
|
+
if (at.get(k, k) < 0) {
|
|
4078
4179
|
s[k] = -s[k];
|
|
4079
4180
|
}
|
|
4080
4181
|
for (let i = k; i < m; i++) {
|
|
4081
|
-
|
|
4182
|
+
at.set(k, i, at.get(k, i) / s[k]);
|
|
4082
4183
|
}
|
|
4083
|
-
|
|
4184
|
+
at.set(k, k, at.get(k, k) + 1);
|
|
4084
4185
|
}
|
|
4085
4186
|
s[k] = -s[k];
|
|
4086
4187
|
}
|
|
@@ -4089,19 +4190,19 @@ class SingularValueDecomposition {
|
|
|
4089
4190
|
if (k < nct && s[k] !== 0) {
|
|
4090
4191
|
let t = 0;
|
|
4091
4192
|
for (let i = k; i < m; i++) {
|
|
4092
|
-
t +=
|
|
4193
|
+
t += at.get(k, i) * at.get(j, i);
|
|
4093
4194
|
}
|
|
4094
|
-
t = -t /
|
|
4195
|
+
t = -t / at.get(k, k);
|
|
4095
4196
|
for (let i = k; i < m; i++) {
|
|
4096
|
-
|
|
4197
|
+
at.set(j, i, at.get(j, i) + t * at.get(k, i));
|
|
4097
4198
|
}
|
|
4098
4199
|
}
|
|
4099
|
-
e[j] =
|
|
4200
|
+
e[j] = at.get(j, k);
|
|
4100
4201
|
}
|
|
4101
4202
|
|
|
4102
4203
|
if (wantu && k < nct) {
|
|
4103
4204
|
for (let i = k; i < m; i++) {
|
|
4104
|
-
U.set(
|
|
4205
|
+
U.set(k, i, at.get(k, i));
|
|
4105
4206
|
}
|
|
4106
4207
|
}
|
|
4107
4208
|
|
|
@@ -4126,19 +4227,19 @@ class SingularValueDecomposition {
|
|
|
4126
4227
|
}
|
|
4127
4228
|
for (let i = k + 1; i < m; i++) {
|
|
4128
4229
|
for (let j = k + 1; j < n; j++) {
|
|
4129
|
-
work[i] += e[j] *
|
|
4230
|
+
work[i] += e[j] * at.get(j, i);
|
|
4130
4231
|
}
|
|
4131
4232
|
}
|
|
4132
4233
|
for (let j = k + 1; j < n; j++) {
|
|
4133
4234
|
let t = -e[j] / e[k + 1];
|
|
4134
4235
|
for (let i = k + 1; i < m; i++) {
|
|
4135
|
-
|
|
4236
|
+
at.set(j, i, at.get(j, i) + t * work[i]);
|
|
4136
4237
|
}
|
|
4137
4238
|
}
|
|
4138
4239
|
}
|
|
4139
4240
|
if (wantv) {
|
|
4140
4241
|
for (let i = k + 1; i < n; i++) {
|
|
4141
|
-
V.set(
|
|
4242
|
+
V.set(k, i, e[i]);
|
|
4142
4243
|
}
|
|
4143
4244
|
}
|
|
4144
4245
|
}
|
|
@@ -4146,20 +4247,20 @@ class SingularValueDecomposition {
|
|
|
4146
4247
|
|
|
4147
4248
|
let p = Math.min(n, m + 1);
|
|
4148
4249
|
if (nct < n) {
|
|
4149
|
-
s[nct] =
|
|
4250
|
+
s[nct] = at.get(nct, nct);
|
|
4150
4251
|
}
|
|
4151
4252
|
if (m < p) {
|
|
4152
4253
|
s[p - 1] = 0;
|
|
4153
4254
|
}
|
|
4154
4255
|
if (nrt + 1 < p) {
|
|
4155
|
-
e[nrt] =
|
|
4256
|
+
e[nrt] = at.get(p - 1, nrt);
|
|
4156
4257
|
}
|
|
4157
4258
|
e[p - 1] = 0;
|
|
4158
4259
|
|
|
4159
4260
|
if (wantu) {
|
|
4160
4261
|
for (let j = nct; j < nu; j++) {
|
|
4161
4262
|
for (let i = 0; i < m; i++) {
|
|
4162
|
-
U.set(
|
|
4263
|
+
U.set(j, i, 0);
|
|
4163
4264
|
}
|
|
4164
4265
|
U.set(j, j, 1);
|
|
4165
4266
|
}
|
|
@@ -4168,23 +4269,23 @@ class SingularValueDecomposition {
|
|
|
4168
4269
|
for (let j = k + 1; j < nu; j++) {
|
|
4169
4270
|
let t = 0;
|
|
4170
4271
|
for (let i = k; i < m; i++) {
|
|
4171
|
-
t += U.get(
|
|
4272
|
+
t += U.get(k, i) * U.get(j, i);
|
|
4172
4273
|
}
|
|
4173
4274
|
t = -t / U.get(k, k);
|
|
4174
4275
|
for (let i = k; i < m; i++) {
|
|
4175
|
-
U.set(
|
|
4276
|
+
U.set(j, i, U.get(j, i) + t * U.get(k, i));
|
|
4176
4277
|
}
|
|
4177
4278
|
}
|
|
4178
4279
|
for (let i = k; i < m; i++) {
|
|
4179
|
-
U.set(
|
|
4280
|
+
U.set(k, i, -U.get(k, i));
|
|
4180
4281
|
}
|
|
4181
4282
|
U.set(k, k, 1 + U.get(k, k));
|
|
4182
4283
|
for (let i = 0; i < k - 1; i++) {
|
|
4183
|
-
U.set(
|
|
4284
|
+
U.set(k, i, 0);
|
|
4184
4285
|
}
|
|
4185
4286
|
} else {
|
|
4186
4287
|
for (let i = 0; i < m; i++) {
|
|
4187
|
-
U.set(
|
|
4288
|
+
U.set(k, i, 0);
|
|
4188
4289
|
}
|
|
4189
4290
|
U.set(k, k, 1);
|
|
4190
4291
|
}
|
|
@@ -4197,16 +4298,16 @@ class SingularValueDecomposition {
|
|
|
4197
4298
|
for (let j = k + 1; j < n; j++) {
|
|
4198
4299
|
let t = 0;
|
|
4199
4300
|
for (let i = k + 1; i < n; i++) {
|
|
4200
|
-
t += V.get(
|
|
4301
|
+
t += V.get(k, i) * V.get(j, i);
|
|
4201
4302
|
}
|
|
4202
|
-
t = -t / V.get(k + 1
|
|
4303
|
+
t = -t / V.get(k, k + 1);
|
|
4203
4304
|
for (let i = k + 1; i < n; i++) {
|
|
4204
|
-
V.set(
|
|
4305
|
+
V.set(j, i, V.get(j, i) + t * V.get(k, i));
|
|
4205
4306
|
}
|
|
4206
4307
|
}
|
|
4207
4308
|
}
|
|
4208
4309
|
for (let i = 0; i < n; i++) {
|
|
4209
|
-
V.set(
|
|
4310
|
+
V.set(k, i, 0);
|
|
4210
4311
|
}
|
|
4211
4312
|
V.set(k, k, 1);
|
|
4212
4313
|
}
|
|
@@ -4270,9 +4371,9 @@ class SingularValueDecomposition {
|
|
|
4270
4371
|
}
|
|
4271
4372
|
if (wantv) {
|
|
4272
4373
|
for (let i = 0; i < n; i++) {
|
|
4273
|
-
t = cs * V.get(
|
|
4274
|
-
V.set(
|
|
4275
|
-
V.set(
|
|
4374
|
+
t = cs * V.get(j, i) + sn * V.get(p - 1, i);
|
|
4375
|
+
V.set(p - 1, i, -sn * V.get(j, i) + cs * V.get(p - 1, i));
|
|
4376
|
+
V.set(j, i, t);
|
|
4276
4377
|
}
|
|
4277
4378
|
}
|
|
4278
4379
|
}
|
|
@@ -4290,9 +4391,9 @@ class SingularValueDecomposition {
|
|
|
4290
4391
|
e[j] = cs * e[j];
|
|
4291
4392
|
if (wantu) {
|
|
4292
4393
|
for (let i = 0; i < m; i++) {
|
|
4293
|
-
t = cs * U.get(
|
|
4294
|
-
U.set(
|
|
4295
|
-
U.set(
|
|
4394
|
+
t = cs * U.get(j, i) + sn * U.get(k - 1, i);
|
|
4395
|
+
U.set(k - 1, i, -sn * U.get(j, i) + cs * U.get(k - 1, i));
|
|
4396
|
+
U.set(j, i, t);
|
|
4296
4397
|
}
|
|
4297
4398
|
}
|
|
4298
4399
|
}
|
|
@@ -4338,9 +4439,9 @@ class SingularValueDecomposition {
|
|
|
4338
4439
|
s[j + 1] = cs * s[j + 1];
|
|
4339
4440
|
if (wantv) {
|
|
4340
4441
|
for (let i = 0; i < n; i++) {
|
|
4341
|
-
t = cs * V.get(
|
|
4342
|
-
V.set(
|
|
4343
|
-
V.set(
|
|
4442
|
+
t = cs * V.get(j, i) + sn * V.get(j + 1, i);
|
|
4443
|
+
V.set(j + 1, i, -sn * V.get(j, i) + cs * V.get(j + 1, i));
|
|
4444
|
+
V.set(j, i, t);
|
|
4344
4445
|
}
|
|
4345
4446
|
}
|
|
4346
4447
|
t = hypotenuse(f, g);
|
|
@@ -4354,9 +4455,9 @@ class SingularValueDecomposition {
|
|
|
4354
4455
|
e[j + 1] = cs * e[j + 1];
|
|
4355
4456
|
if (wantu && j < m - 1) {
|
|
4356
4457
|
for (let i = 0; i < m; i++) {
|
|
4357
|
-
t = cs * U.get(
|
|
4358
|
-
U.set(
|
|
4359
|
-
U.set(
|
|
4458
|
+
t = cs * U.get(j, i) + sn * U.get(j + 1, i);
|
|
4459
|
+
U.set(j + 1, i, -sn * U.get(j, i) + cs * U.get(j + 1, i));
|
|
4460
|
+
U.set(j, i, t);
|
|
4360
4461
|
}
|
|
4361
4462
|
}
|
|
4362
4463
|
}
|
|
@@ -4368,7 +4469,7 @@ class SingularValueDecomposition {
|
|
|
4368
4469
|
s[k] = s[k] < 0 ? -s[k] : 0;
|
|
4369
4470
|
if (wantv) {
|
|
4370
4471
|
for (let i = 0; i <= pp; i++) {
|
|
4371
|
-
V.set(
|
|
4472
|
+
V.set(k, i, -V.get(k, i));
|
|
4372
4473
|
}
|
|
4373
4474
|
}
|
|
4374
4475
|
}
|
|
@@ -4381,16 +4482,16 @@ class SingularValueDecomposition {
|
|
|
4381
4482
|
s[k + 1] = t;
|
|
4382
4483
|
if (wantv && k < n - 1) {
|
|
4383
4484
|
for (let i = 0; i < n; i++) {
|
|
4384
|
-
t = V.get(
|
|
4385
|
-
V.set(
|
|
4386
|
-
V.set(
|
|
4485
|
+
t = V.get(k + 1, i);
|
|
4486
|
+
V.set(k + 1, i, V.get(k, i));
|
|
4487
|
+
V.set(k, i, t);
|
|
4387
4488
|
}
|
|
4388
4489
|
}
|
|
4389
4490
|
if (wantu && k < m - 1) {
|
|
4390
4491
|
for (let i = 0; i < m; i++) {
|
|
4391
|
-
t = U.get(
|
|
4392
|
-
U.set(
|
|
4393
|
-
U.set(
|
|
4492
|
+
t = U.get(k + 1, i);
|
|
4493
|
+
U.set(k + 1, i, U.get(k, i));
|
|
4494
|
+
U.set(k, i, t);
|
|
4394
4495
|
}
|
|
4395
4496
|
}
|
|
4396
4497
|
k++;
|
|
@@ -4402,6 +4503,13 @@ class SingularValueDecomposition {
|
|
|
4402
4503
|
}
|
|
4403
4504
|
}
|
|
4404
4505
|
|
|
4506
|
+
// Restore the logical (row-major) layout of the singular vectors, which were
|
|
4507
|
+
// accumulated in transposed storage for cache-sequential inner loops. V is
|
|
4508
|
+
// always square and U is square whenever the input is, so this is done in
|
|
4509
|
+
// place (no allocation) in the common case.
|
|
4510
|
+
U = U.isSquare() ? transposeSquareInPlace(U) : U.transpose();
|
|
4511
|
+
V = transposeSquareInPlace(V);
|
|
4512
|
+
|
|
4405
4513
|
if (swapped) {
|
|
4406
4514
|
let tmp = V;
|
|
4407
4515
|
V = U;
|
|
@@ -4777,14 +4885,25 @@ class EigenvalueDecomposition {
|
|
|
4777
4885
|
}
|
|
4778
4886
|
|
|
4779
4887
|
if (isSymmetric) {
|
|
4888
|
+
// tred2/tql2 access V almost exclusively down columns (the row index
|
|
4889
|
+
// varies in the hot loops). Storing V transposed turns those into
|
|
4890
|
+
// sequential row scans of the row-major backing store; we transpose it
|
|
4891
|
+
// back to the logical layout before returning. V.get(j, i) holds the
|
|
4892
|
+
// logical V(i, j).
|
|
4780
4893
|
for (i = 0; i < n; i++) {
|
|
4781
4894
|
for (j = 0; j < n; j++) {
|
|
4782
|
-
V.set(
|
|
4895
|
+
V.set(j, i, value.get(i, j));
|
|
4783
4896
|
}
|
|
4784
4897
|
}
|
|
4785
4898
|
tred2(n, e, d, V);
|
|
4786
4899
|
tql2(n, e, d, V);
|
|
4900
|
+
// V is square; restore the logical layout in place (no allocation).
|
|
4901
|
+
transposeSquareInPlace(V);
|
|
4787
4902
|
} else {
|
|
4903
|
+
// The non-symmetric path (orthes/hqr2) has two O(n^3) phases with opposite
|
|
4904
|
+
// memory-layout preferences (the QR sweep favours column-major eigenvectors
|
|
4905
|
+
// while the back-transform favours row-major), so a single transposed
|
|
4906
|
+
// storage cannot help both. It is left in the original row-major layout.
|
|
4788
4907
|
let H = new Matrix(n, n);
|
|
4789
4908
|
let ort = new Float64Array(n);
|
|
4790
4909
|
for (j = 0; j < n; j++) {
|
|
@@ -4839,7 +4958,7 @@ function tred2(n, e, d, V) {
|
|
|
4839
4958
|
let f, g, h, i, j, k, hh, scale;
|
|
4840
4959
|
|
|
4841
4960
|
for (j = 0; j < n; j++) {
|
|
4842
|
-
d[j] = V.get(n - 1
|
|
4961
|
+
d[j] = V.get(j, n - 1);
|
|
4843
4962
|
}
|
|
4844
4963
|
|
|
4845
4964
|
for (i = n - 1; i > 0; i--) {
|
|
@@ -4852,9 +4971,9 @@ function tred2(n, e, d, V) {
|
|
|
4852
4971
|
if (scale === 0) {
|
|
4853
4972
|
e[i] = d[i - 1];
|
|
4854
4973
|
for (j = 0; j < i; j++) {
|
|
4855
|
-
d[j] = V.get(i - 1
|
|
4856
|
-
V.set(i, j, 0);
|
|
4974
|
+
d[j] = V.get(j, i - 1);
|
|
4857
4975
|
V.set(j, i, 0);
|
|
4976
|
+
V.set(i, j, 0);
|
|
4858
4977
|
}
|
|
4859
4978
|
} else {
|
|
4860
4979
|
for (k = 0; k < i; k++) {
|
|
@@ -4877,11 +4996,11 @@ function tred2(n, e, d, V) {
|
|
|
4877
4996
|
|
|
4878
4997
|
for (j = 0; j < i; j++) {
|
|
4879
4998
|
f = d[j];
|
|
4880
|
-
V.set(
|
|
4999
|
+
V.set(i, j, f);
|
|
4881
5000
|
g = e[j] + V.get(j, j) * f;
|
|
4882
5001
|
for (k = j + 1; k <= i - 1; k++) {
|
|
4883
|
-
g += V.get(
|
|
4884
|
-
e[k] += V.get(
|
|
5002
|
+
g += V.get(j, k) * d[k];
|
|
5003
|
+
e[k] += V.get(j, k) * f;
|
|
4885
5004
|
}
|
|
4886
5005
|
e[j] = g;
|
|
4887
5006
|
}
|
|
@@ -4901,43 +5020,43 @@ function tred2(n, e, d, V) {
|
|
|
4901
5020
|
f = d[j];
|
|
4902
5021
|
g = e[j];
|
|
4903
5022
|
for (k = j; k <= i - 1; k++) {
|
|
4904
|
-
V.set(
|
|
5023
|
+
V.set(j, k, V.get(j, k) - (f * e[k] + g * d[k]));
|
|
4905
5024
|
}
|
|
4906
|
-
d[j] = V.get(i - 1
|
|
4907
|
-
V.set(
|
|
5025
|
+
d[j] = V.get(j, i - 1);
|
|
5026
|
+
V.set(j, i, 0);
|
|
4908
5027
|
}
|
|
4909
5028
|
}
|
|
4910
5029
|
d[i] = h;
|
|
4911
5030
|
}
|
|
4912
5031
|
|
|
4913
5032
|
for (i = 0; i < n - 1; i++) {
|
|
4914
|
-
V.set(n - 1,
|
|
5033
|
+
V.set(i, n - 1, V.get(i, i));
|
|
4915
5034
|
V.set(i, i, 1);
|
|
4916
5035
|
h = d[i + 1];
|
|
4917
5036
|
if (h !== 0) {
|
|
4918
5037
|
for (k = 0; k <= i; k++) {
|
|
4919
|
-
d[k] = V.get(
|
|
5038
|
+
d[k] = V.get(i + 1, k) / h;
|
|
4920
5039
|
}
|
|
4921
5040
|
|
|
4922
5041
|
for (j = 0; j <= i; j++) {
|
|
4923
5042
|
g = 0;
|
|
4924
5043
|
for (k = 0; k <= i; k++) {
|
|
4925
|
-
g += V.get(
|
|
5044
|
+
g += V.get(i + 1, k) * V.get(j, k);
|
|
4926
5045
|
}
|
|
4927
5046
|
for (k = 0; k <= i; k++) {
|
|
4928
|
-
V.set(
|
|
5047
|
+
V.set(j, k, V.get(j, k) - g * d[k]);
|
|
4929
5048
|
}
|
|
4930
5049
|
}
|
|
4931
5050
|
}
|
|
4932
5051
|
|
|
4933
5052
|
for (k = 0; k <= i; k++) {
|
|
4934
|
-
V.set(
|
|
5053
|
+
V.set(i + 1, k, 0);
|
|
4935
5054
|
}
|
|
4936
5055
|
}
|
|
4937
5056
|
|
|
4938
5057
|
for (j = 0; j < n; j++) {
|
|
4939
|
-
d[j] = V.get(n - 1
|
|
4940
|
-
V.set(n - 1,
|
|
5058
|
+
d[j] = V.get(j, n - 1);
|
|
5059
|
+
V.set(j, n - 1, 0);
|
|
4941
5060
|
}
|
|
4942
5061
|
|
|
4943
5062
|
V.set(n - 1, n - 1, 1);
|
|
@@ -5008,9 +5127,9 @@ function tql2(n, e, d, V) {
|
|
|
5008
5127
|
d[i + 1] = h + s * (c * g + s * d[i]);
|
|
5009
5128
|
|
|
5010
5129
|
for (k = 0; k < n; k++) {
|
|
5011
|
-
h = V.get(
|
|
5012
|
-
V.set(
|
|
5013
|
-
V.set(
|
|
5130
|
+
h = V.get(i + 1, k);
|
|
5131
|
+
V.set(i + 1, k, s * V.get(i, k) + c * h);
|
|
5132
|
+
V.set(i, k, c * V.get(i, k) - s * h);
|
|
5014
5133
|
}
|
|
5015
5134
|
}
|
|
5016
5135
|
|
|
@@ -5037,9 +5156,9 @@ function tql2(n, e, d, V) {
|
|
|
5037
5156
|
d[k] = d[i];
|
|
5038
5157
|
d[i] = p;
|
|
5039
5158
|
for (j = 0; j < n; j++) {
|
|
5040
|
-
p = V.get(
|
|
5041
|
-
V.set(
|
|
5042
|
-
V.set(
|
|
5159
|
+
p = V.get(i, j);
|
|
5160
|
+
V.set(i, j, V.get(k, j));
|
|
5161
|
+
V.set(k, j, p);
|
|
5043
5162
|
}
|
|
5044
5163
|
}
|
|
5045
5164
|
}
|