ml-matrix 6.12.1 → 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.js CHANGED
@@ -2,8 +2,114 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var isAnyArray = require('is-any-array');
6
- var rescale = require('ml-array-rescale');
5
+ // eslint-disable-next-line @typescript-eslint/unbound-method
6
+ const toString = Object.prototype.toString;
7
+ /**
8
+ * Checks if an object is an instance of an Array (array or typed array, except those that contain bigint values).
9
+ * @param value - Object to check.
10
+ * @returns True if the object is an array or a typed array.
11
+ */
12
+ function isAnyArray(value) {
13
+ const tag = toString.call(value);
14
+ return tag.endsWith('Array]') && !tag.includes('Big');
15
+ }
16
+
17
+ /**
18
+ * Computes the maximum of the given values.
19
+ *
20
+ * @param input
21
+ * @param options
22
+ */
23
+ function max(input, options = {}) {
24
+ if (!isAnyArray(input)) {
25
+ throw new TypeError('input must be an array');
26
+ }
27
+ if (input.length === 0) {
28
+ throw new TypeError('input must not be empty');
29
+ }
30
+ const { fromIndex = 0, toIndex = input.length } = options;
31
+ if (fromIndex < 0 ||
32
+ fromIndex >= input.length ||
33
+ !Number.isInteger(fromIndex)) {
34
+ throw new Error('fromIndex must be a positive integer smaller than length');
35
+ }
36
+ if (toIndex <= fromIndex ||
37
+ toIndex > input.length ||
38
+ !Number.isInteger(toIndex)) {
39
+ throw new Error('toIndex must be an integer greater than fromIndex and at most equal to length');
40
+ }
41
+ let maxValue = input[fromIndex];
42
+ for (let i = fromIndex + 1; i < toIndex; i++) {
43
+ if (input[i] > maxValue)
44
+ maxValue = input[i];
45
+ }
46
+ return maxValue;
47
+ }
48
+
49
+ /**
50
+ * Computes the minimum of the given values.
51
+ */
52
+ function min(input, options = {}) {
53
+ if (!isAnyArray(input)) {
54
+ throw new TypeError('input must be an array');
55
+ }
56
+ if (input.length === 0) {
57
+ throw new TypeError('input must not be empty');
58
+ }
59
+ const { fromIndex = 0, toIndex = input.length } = options;
60
+ if (fromIndex < 0 ||
61
+ fromIndex >= input.length ||
62
+ !Number.isInteger(fromIndex)) {
63
+ throw new Error('fromIndex must be a positive integer smaller than length');
64
+ }
65
+ if (toIndex <= fromIndex ||
66
+ toIndex > input.length ||
67
+ !Number.isInteger(toIndex)) {
68
+ throw new Error('toIndex must be an integer greater than fromIndex and at most equal to length');
69
+ }
70
+ let minValue = input[fromIndex];
71
+ for (let i = fromIndex + 1; i < toIndex; i++) {
72
+ if (input[i] < minValue)
73
+ minValue = input[i];
74
+ }
75
+ return minValue;
76
+ }
77
+
78
+ /**
79
+ * Rescale an array into a range.
80
+ */
81
+ function rescale(input, options = {}) {
82
+ if (!isAnyArray(input)) {
83
+ throw new TypeError('input must be an array');
84
+ }
85
+ else if (input.length === 0) {
86
+ throw new TypeError('input must not be empty');
87
+ }
88
+ let output;
89
+ if (options.output !== undefined) {
90
+ if (!isAnyArray(options.output)) {
91
+ throw new TypeError('output option must be an array if specified');
92
+ }
93
+ output = options.output;
94
+ }
95
+ else {
96
+ output = new Array(input.length);
97
+ }
98
+ const currentMin = min(input);
99
+ const currentMax = max(input);
100
+ if (currentMin === currentMax) {
101
+ throw new RangeError('minimum and maximum input values are equal. Cannot rescale a constant array');
102
+ }
103
+ const { min: minValue = options.autoMinMax ? currentMin : 0, max: maxValue = options.autoMinMax ? currentMax : 1, } = options;
104
+ if (minValue >= maxValue) {
105
+ throw new RangeError('min option must be smaller than max option');
106
+ }
107
+ const factor = (maxValue - minValue) / (currentMax - currentMin);
108
+ for (let i = 0; i < input.length; i++) {
109
+ output[i] = (input[i] - currentMin) * factor + minValue;
110
+ }
111
+ return output;
112
+ }
7
113
 
8
114
  const indent = ' '.repeat(2);
9
115
  const indentData = ' '.repeat(4);
@@ -992,7 +1098,7 @@ function checkColumnVector(matrix, vector) {
992
1098
  }
993
1099
 
994
1100
  function checkRowIndices(matrix, rowIndices) {
995
- if (!isAnyArray.isAnyArray(rowIndices)) {
1101
+ if (!isAnyArray(rowIndices)) {
996
1102
  throw new TypeError('row indices must be an array');
997
1103
  }
998
1104
 
@@ -1004,7 +1110,7 @@ function checkRowIndices(matrix, rowIndices) {
1004
1110
  }
1005
1111
 
1006
1112
  function checkColumnIndices(matrix, columnIndices) {
1007
- if (!isAnyArray.isAnyArray(columnIndices)) {
1113
+ if (!isAnyArray(columnIndices)) {
1008
1114
  throw new TypeError('column indices must be an array');
1009
1115
  }
1010
1116
 
@@ -2110,6 +2216,78 @@ class AbstractMatrix {
2110
2216
  return result;
2111
2217
  }
2112
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
+
2113
2291
  mpow(scalar) {
2114
2292
  if (!this.isSquare()) {
2115
2293
  throw new RangeError('Matrix must be square');
@@ -2659,13 +2837,13 @@ class AbstractMatrix {
2659
2837
  }
2660
2838
  switch (by) {
2661
2839
  case 'row': {
2662
- if (!isAnyArray.isAnyArray(mean)) {
2840
+ if (!isAnyArray(mean)) {
2663
2841
  throw new TypeError('mean must be an array');
2664
2842
  }
2665
2843
  return varianceByRow(this, unbiased, mean);
2666
2844
  }
2667
2845
  case 'column': {
2668
- if (!isAnyArray.isAnyArray(mean)) {
2846
+ if (!isAnyArray(mean)) {
2669
2847
  throw new TypeError('mean must be an array');
2670
2848
  }
2671
2849
  return varianceByColumn(this, unbiased, mean);
@@ -2708,14 +2886,14 @@ class AbstractMatrix {
2708
2886
  const { center = this.mean(by) } = options;
2709
2887
  switch (by) {
2710
2888
  case 'row': {
2711
- if (!isAnyArray.isAnyArray(center)) {
2889
+ if (!isAnyArray(center)) {
2712
2890
  throw new TypeError('center must be an array');
2713
2891
  }
2714
2892
  centerByRow(this, center);
2715
2893
  return this;
2716
2894
  }
2717
2895
  case 'column': {
2718
- if (!isAnyArray.isAnyArray(center)) {
2896
+ if (!isAnyArray(center)) {
2719
2897
  throw new TypeError('center must be an array');
2720
2898
  }
2721
2899
  centerByColumn(this, center);
@@ -2746,7 +2924,7 @@ class AbstractMatrix {
2746
2924
  case 'row': {
2747
2925
  if (scale === undefined) {
2748
2926
  scale = getScaleByRow(this);
2749
- } else if (!isAnyArray.isAnyArray(scale)) {
2927
+ } else if (!isAnyArray(scale)) {
2750
2928
  throw new TypeError('scale must be an array');
2751
2929
  }
2752
2930
  scaleByRow(this, scale);
@@ -2755,7 +2933,7 @@ class AbstractMatrix {
2755
2933
  case 'column': {
2756
2934
  if (scale === undefined) {
2757
2935
  scale = getScaleByColumn(this);
2758
- } else if (!isAnyArray.isAnyArray(scale)) {
2936
+ } else if (!isAnyArray(scale)) {
2759
2937
  throw new TypeError('scale must be an array');
2760
2938
  }
2761
2939
  scaleByColumn(this, scale);
@@ -2869,7 +3047,7 @@ class Matrix extends AbstractMatrix {
2869
3047
  Matrix.copy(nRows, this);
2870
3048
  } else if (Number.isInteger(nRows) && nRows >= 0) {
2871
3049
  this.#initData(nRows, nColumns);
2872
- } else if (isAnyArray.isAnyArray(nRows)) {
3050
+ } else if (isAnyArray(nRows)) {
2873
3051
  // Copy the values from the 2D array
2874
3052
  const arrayData = nRows;
2875
3053
  nRows = arrayData.length;
@@ -3565,8 +3743,8 @@ class WrapperMatrix2D extends AbstractMatrix {
3565
3743
  }
3566
3744
 
3567
3745
  function wrap(array, options) {
3568
- if (isAnyArray.isAnyArray(array)) {
3569
- if (array[0] && isAnyArray.isAnyArray(array[0])) {
3746
+ if (isAnyArray(array)) {
3747
+ if (array[0] && isAnyArray(array[0])) {
3570
3748
  return new WrapperMatrix2D(array);
3571
3749
  } else {
3572
3750
  return new WrapperMatrix1D(array, options);
@@ -3745,6 +3923,27 @@ class LuDecomposition {
3745
3923
  }
3746
3924
  }
3747
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
+
3748
3947
  function hypotenuse(a, b) {
3749
3948
  let r = 0;
3750
3949
  if (Math.abs(a) > Math.abs(b)) {
@@ -3923,32 +4122,40 @@ class SingularValueDecomposition {
3923
4122
  let wantu = Boolean(computeLeftSingularVectors);
3924
4123
  let wantv = Boolean(computeRightSingularVectors);
3925
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.
3926
4129
  let swapped = false;
3927
- let a;
4130
+ let at;
3928
4131
  if (m < n) {
3929
4132
  if (!autoTranspose) {
3930
- a = value.clone();
3931
4133
  // eslint-disable-next-line no-console
3932
4134
  console.warn(
3933
4135
  'Computing SVD on a matrix with more columns than rows. Consider enabling autoTranspose',
3934
4136
  );
4137
+ at = value.transpose();
3935
4138
  } else {
3936
- a = value.transpose();
3937
- m = a.rows;
3938
- n = a.columns;
4139
+ at = value.clone();
4140
+ m = value.columns;
4141
+ n = value.rows;
3939
4142
  swapped = true;
3940
4143
  let aux = wantu;
3941
4144
  wantu = wantv;
3942
4145
  wantv = aux;
3943
4146
  }
3944
4147
  } else {
3945
- a = value.clone();
4148
+ at = value.transpose();
3946
4149
  }
3947
4150
 
3948
4151
  let nu = Math.min(m, n);
3949
4152
  let ni = Math.min(m + 1, n);
3950
4153
  let s = new Float64Array(ni);
3951
- let U = new Matrix(m, nu);
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);
3952
4159
  let V = new Matrix(n, n);
3953
4160
 
3954
4161
  let e = new Float64Array(n);
@@ -3965,16 +4172,16 @@ class SingularValueDecomposition {
3965
4172
  if (k < nct) {
3966
4173
  s[k] = 0;
3967
4174
  for (let i = k; i < m; i++) {
3968
- s[k] = hypotenuse(s[k], a.get(i, k));
4175
+ s[k] = hypotenuse(s[k], at.get(k, i));
3969
4176
  }
3970
4177
  if (s[k] !== 0) {
3971
- if (a.get(k, k) < 0) {
4178
+ if (at.get(k, k) < 0) {
3972
4179
  s[k] = -s[k];
3973
4180
  }
3974
4181
  for (let i = k; i < m; i++) {
3975
- a.set(i, k, a.get(i, k) / s[k]);
4182
+ at.set(k, i, at.get(k, i) / s[k]);
3976
4183
  }
3977
- a.set(k, k, a.get(k, k) + 1);
4184
+ at.set(k, k, at.get(k, k) + 1);
3978
4185
  }
3979
4186
  s[k] = -s[k];
3980
4187
  }
@@ -3983,19 +4190,19 @@ class SingularValueDecomposition {
3983
4190
  if (k < nct && s[k] !== 0) {
3984
4191
  let t = 0;
3985
4192
  for (let i = k; i < m; i++) {
3986
- t += a.get(i, k) * a.get(i, j);
4193
+ t += at.get(k, i) * at.get(j, i);
3987
4194
  }
3988
- t = -t / a.get(k, k);
4195
+ t = -t / at.get(k, k);
3989
4196
  for (let i = k; i < m; i++) {
3990
- a.set(i, j, a.get(i, j) + t * a.get(i, k));
4197
+ at.set(j, i, at.get(j, i) + t * at.get(k, i));
3991
4198
  }
3992
4199
  }
3993
- e[j] = a.get(k, j);
4200
+ e[j] = at.get(j, k);
3994
4201
  }
3995
4202
 
3996
4203
  if (wantu && k < nct) {
3997
4204
  for (let i = k; i < m; i++) {
3998
- U.set(i, k, a.get(i, k));
4205
+ U.set(k, i, at.get(k, i));
3999
4206
  }
4000
4207
  }
4001
4208
 
@@ -4020,19 +4227,19 @@ class SingularValueDecomposition {
4020
4227
  }
4021
4228
  for (let i = k + 1; i < m; i++) {
4022
4229
  for (let j = k + 1; j < n; j++) {
4023
- work[i] += e[j] * a.get(i, j);
4230
+ work[i] += e[j] * at.get(j, i);
4024
4231
  }
4025
4232
  }
4026
4233
  for (let j = k + 1; j < n; j++) {
4027
4234
  let t = -e[j] / e[k + 1];
4028
4235
  for (let i = k + 1; i < m; i++) {
4029
- a.set(i, j, a.get(i, j) + t * work[i]);
4236
+ at.set(j, i, at.get(j, i) + t * work[i]);
4030
4237
  }
4031
4238
  }
4032
4239
  }
4033
4240
  if (wantv) {
4034
4241
  for (let i = k + 1; i < n; i++) {
4035
- V.set(i, k, e[i]);
4242
+ V.set(k, i, e[i]);
4036
4243
  }
4037
4244
  }
4038
4245
  }
@@ -4040,20 +4247,20 @@ class SingularValueDecomposition {
4040
4247
 
4041
4248
  let p = Math.min(n, m + 1);
4042
4249
  if (nct < n) {
4043
- s[nct] = a.get(nct, nct);
4250
+ s[nct] = at.get(nct, nct);
4044
4251
  }
4045
4252
  if (m < p) {
4046
4253
  s[p - 1] = 0;
4047
4254
  }
4048
4255
  if (nrt + 1 < p) {
4049
- e[nrt] = a.get(nrt, p - 1);
4256
+ e[nrt] = at.get(p - 1, nrt);
4050
4257
  }
4051
4258
  e[p - 1] = 0;
4052
4259
 
4053
4260
  if (wantu) {
4054
4261
  for (let j = nct; j < nu; j++) {
4055
4262
  for (let i = 0; i < m; i++) {
4056
- U.set(i, j, 0);
4263
+ U.set(j, i, 0);
4057
4264
  }
4058
4265
  U.set(j, j, 1);
4059
4266
  }
@@ -4062,23 +4269,23 @@ class SingularValueDecomposition {
4062
4269
  for (let j = k + 1; j < nu; j++) {
4063
4270
  let t = 0;
4064
4271
  for (let i = k; i < m; i++) {
4065
- t += U.get(i, k) * U.get(i, j);
4272
+ t += U.get(k, i) * U.get(j, i);
4066
4273
  }
4067
4274
  t = -t / U.get(k, k);
4068
4275
  for (let i = k; i < m; i++) {
4069
- U.set(i, j, U.get(i, j) + t * U.get(i, k));
4276
+ U.set(j, i, U.get(j, i) + t * U.get(k, i));
4070
4277
  }
4071
4278
  }
4072
4279
  for (let i = k; i < m; i++) {
4073
- U.set(i, k, -U.get(i, k));
4280
+ U.set(k, i, -U.get(k, i));
4074
4281
  }
4075
4282
  U.set(k, k, 1 + U.get(k, k));
4076
4283
  for (let i = 0; i < k - 1; i++) {
4077
- U.set(i, k, 0);
4284
+ U.set(k, i, 0);
4078
4285
  }
4079
4286
  } else {
4080
4287
  for (let i = 0; i < m; i++) {
4081
- U.set(i, k, 0);
4288
+ U.set(k, i, 0);
4082
4289
  }
4083
4290
  U.set(k, k, 1);
4084
4291
  }
@@ -4091,16 +4298,16 @@ class SingularValueDecomposition {
4091
4298
  for (let j = k + 1; j < n; j++) {
4092
4299
  let t = 0;
4093
4300
  for (let i = k + 1; i < n; i++) {
4094
- t += V.get(i, k) * V.get(i, j);
4301
+ t += V.get(k, i) * V.get(j, i);
4095
4302
  }
4096
- t = -t / V.get(k + 1, k);
4303
+ t = -t / V.get(k, k + 1);
4097
4304
  for (let i = k + 1; i < n; i++) {
4098
- V.set(i, j, V.get(i, j) + t * V.get(i, k));
4305
+ V.set(j, i, V.get(j, i) + t * V.get(k, i));
4099
4306
  }
4100
4307
  }
4101
4308
  }
4102
4309
  for (let i = 0; i < n; i++) {
4103
- V.set(i, k, 0);
4310
+ V.set(k, i, 0);
4104
4311
  }
4105
4312
  V.set(k, k, 1);
4106
4313
  }
@@ -4164,9 +4371,9 @@ class SingularValueDecomposition {
4164
4371
  }
4165
4372
  if (wantv) {
4166
4373
  for (let i = 0; i < n; i++) {
4167
- t = cs * V.get(i, j) + sn * V.get(i, p - 1);
4168
- V.set(i, p - 1, -sn * V.get(i, j) + cs * V.get(i, p - 1));
4169
- V.set(i, j, t);
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);
4170
4377
  }
4171
4378
  }
4172
4379
  }
@@ -4184,9 +4391,9 @@ class SingularValueDecomposition {
4184
4391
  e[j] = cs * e[j];
4185
4392
  if (wantu) {
4186
4393
  for (let i = 0; i < m; i++) {
4187
- t = cs * U.get(i, j) + sn * U.get(i, k - 1);
4188
- U.set(i, k - 1, -sn * U.get(i, j) + cs * U.get(i, k - 1));
4189
- U.set(i, j, t);
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);
4190
4397
  }
4191
4398
  }
4192
4399
  }
@@ -4232,9 +4439,9 @@ class SingularValueDecomposition {
4232
4439
  s[j + 1] = cs * s[j + 1];
4233
4440
  if (wantv) {
4234
4441
  for (let i = 0; i < n; i++) {
4235
- t = cs * V.get(i, j) + sn * V.get(i, j + 1);
4236
- V.set(i, j + 1, -sn * V.get(i, j) + cs * V.get(i, j + 1));
4237
- V.set(i, j, t);
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);
4238
4445
  }
4239
4446
  }
4240
4447
  t = hypotenuse(f, g);
@@ -4248,9 +4455,9 @@ class SingularValueDecomposition {
4248
4455
  e[j + 1] = cs * e[j + 1];
4249
4456
  if (wantu && j < m - 1) {
4250
4457
  for (let i = 0; i < m; i++) {
4251
- t = cs * U.get(i, j) + sn * U.get(i, j + 1);
4252
- U.set(i, j + 1, -sn * U.get(i, j) + cs * U.get(i, j + 1));
4253
- U.set(i, j, t);
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);
4254
4461
  }
4255
4462
  }
4256
4463
  }
@@ -4262,7 +4469,7 @@ class SingularValueDecomposition {
4262
4469
  s[k] = s[k] < 0 ? -s[k] : 0;
4263
4470
  if (wantv) {
4264
4471
  for (let i = 0; i <= pp; i++) {
4265
- V.set(i, k, -V.get(i, k));
4472
+ V.set(k, i, -V.get(k, i));
4266
4473
  }
4267
4474
  }
4268
4475
  }
@@ -4275,16 +4482,16 @@ class SingularValueDecomposition {
4275
4482
  s[k + 1] = t;
4276
4483
  if (wantv && k < n - 1) {
4277
4484
  for (let i = 0; i < n; i++) {
4278
- t = V.get(i, k + 1);
4279
- V.set(i, k + 1, V.get(i, k));
4280
- V.set(i, k, t);
4485
+ t = V.get(k + 1, i);
4486
+ V.set(k + 1, i, V.get(k, i));
4487
+ V.set(k, i, t);
4281
4488
  }
4282
4489
  }
4283
4490
  if (wantu && k < m - 1) {
4284
4491
  for (let i = 0; i < m; i++) {
4285
- t = U.get(i, k + 1);
4286
- U.set(i, k + 1, U.get(i, k));
4287
- U.set(i, k, t);
4492
+ t = U.get(k + 1, i);
4493
+ U.set(k + 1, i, U.get(k, i));
4494
+ U.set(k, i, t);
4288
4495
  }
4289
4496
  }
4290
4497
  k++;
@@ -4296,6 +4503,13 @@ class SingularValueDecomposition {
4296
4503
  }
4297
4504
  }
4298
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
+
4299
4513
  if (swapped) {
4300
4514
  let tmp = V;
4301
4515
  V = U;
@@ -4566,7 +4780,7 @@ function covariance(xMatrix, yMatrix = xMatrix, options = {}) {
4566
4780
  if (
4567
4781
  typeof yMatrix === 'object' &&
4568
4782
  !Matrix.isMatrix(yMatrix) &&
4569
- !isAnyArray.isAnyArray(yMatrix)
4783
+ !isAnyArray(yMatrix)
4570
4784
  ) {
4571
4785
  options = yMatrix;
4572
4786
  yMatrix = xMatrix;
@@ -4599,7 +4813,7 @@ function correlation(xMatrix, yMatrix = xMatrix, options = {}) {
4599
4813
  if (
4600
4814
  typeof yMatrix === 'object' &&
4601
4815
  !Matrix.isMatrix(yMatrix) &&
4602
- !isAnyArray.isAnyArray(yMatrix)
4816
+ !isAnyArray(yMatrix)
4603
4817
  ) {
4604
4818
  options = yMatrix;
4605
4819
  yMatrix = xMatrix;
@@ -4671,14 +4885,25 @@ class EigenvalueDecomposition {
4671
4885
  }
4672
4886
 
4673
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).
4674
4893
  for (i = 0; i < n; i++) {
4675
4894
  for (j = 0; j < n; j++) {
4676
- V.set(i, j, value.get(i, j));
4895
+ V.set(j, i, value.get(i, j));
4677
4896
  }
4678
4897
  }
4679
4898
  tred2(n, e, d, V);
4680
4899
  tql2(n, e, d, V);
4900
+ // V is square; restore the logical layout in place (no allocation).
4901
+ transposeSquareInPlace(V);
4681
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.
4682
4907
  let H = new Matrix(n, n);
4683
4908
  let ort = new Float64Array(n);
4684
4909
  for (j = 0; j < n; j++) {
@@ -4733,7 +4958,7 @@ function tred2(n, e, d, V) {
4733
4958
  let f, g, h, i, j, k, hh, scale;
4734
4959
 
4735
4960
  for (j = 0; j < n; j++) {
4736
- d[j] = V.get(n - 1, j);
4961
+ d[j] = V.get(j, n - 1);
4737
4962
  }
4738
4963
 
4739
4964
  for (i = n - 1; i > 0; i--) {
@@ -4746,9 +4971,9 @@ function tred2(n, e, d, V) {
4746
4971
  if (scale === 0) {
4747
4972
  e[i] = d[i - 1];
4748
4973
  for (j = 0; j < i; j++) {
4749
- d[j] = V.get(i - 1, j);
4750
- V.set(i, j, 0);
4974
+ d[j] = V.get(j, i - 1);
4751
4975
  V.set(j, i, 0);
4976
+ V.set(i, j, 0);
4752
4977
  }
4753
4978
  } else {
4754
4979
  for (k = 0; k < i; k++) {
@@ -4771,11 +4996,11 @@ function tred2(n, e, d, V) {
4771
4996
 
4772
4997
  for (j = 0; j < i; j++) {
4773
4998
  f = d[j];
4774
- V.set(j, i, f);
4999
+ V.set(i, j, f);
4775
5000
  g = e[j] + V.get(j, j) * f;
4776
5001
  for (k = j + 1; k <= i - 1; k++) {
4777
- g += V.get(k, j) * d[k];
4778
- e[k] += V.get(k, j) * f;
5002
+ g += V.get(j, k) * d[k];
5003
+ e[k] += V.get(j, k) * f;
4779
5004
  }
4780
5005
  e[j] = g;
4781
5006
  }
@@ -4795,43 +5020,43 @@ function tred2(n, e, d, V) {
4795
5020
  f = d[j];
4796
5021
  g = e[j];
4797
5022
  for (k = j; k <= i - 1; k++) {
4798
- V.set(k, j, V.get(k, j) - (f * e[k] + g * d[k]));
5023
+ V.set(j, k, V.get(j, k) - (f * e[k] + g * d[k]));
4799
5024
  }
4800
- d[j] = V.get(i - 1, j);
4801
- V.set(i, j, 0);
5025
+ d[j] = V.get(j, i - 1);
5026
+ V.set(j, i, 0);
4802
5027
  }
4803
5028
  }
4804
5029
  d[i] = h;
4805
5030
  }
4806
5031
 
4807
5032
  for (i = 0; i < n - 1; i++) {
4808
- V.set(n - 1, i, V.get(i, i));
5033
+ V.set(i, n - 1, V.get(i, i));
4809
5034
  V.set(i, i, 1);
4810
5035
  h = d[i + 1];
4811
5036
  if (h !== 0) {
4812
5037
  for (k = 0; k <= i; k++) {
4813
- d[k] = V.get(k, i + 1) / h;
5038
+ d[k] = V.get(i + 1, k) / h;
4814
5039
  }
4815
5040
 
4816
5041
  for (j = 0; j <= i; j++) {
4817
5042
  g = 0;
4818
5043
  for (k = 0; k <= i; k++) {
4819
- g += V.get(k, i + 1) * V.get(k, j);
5044
+ g += V.get(i + 1, k) * V.get(j, k);
4820
5045
  }
4821
5046
  for (k = 0; k <= i; k++) {
4822
- V.set(k, j, V.get(k, j) - g * d[k]);
5047
+ V.set(j, k, V.get(j, k) - g * d[k]);
4823
5048
  }
4824
5049
  }
4825
5050
  }
4826
5051
 
4827
5052
  for (k = 0; k <= i; k++) {
4828
- V.set(k, i + 1, 0);
5053
+ V.set(i + 1, k, 0);
4829
5054
  }
4830
5055
  }
4831
5056
 
4832
5057
  for (j = 0; j < n; j++) {
4833
- d[j] = V.get(n - 1, j);
4834
- V.set(n - 1, j, 0);
5058
+ d[j] = V.get(j, n - 1);
5059
+ V.set(j, n - 1, 0);
4835
5060
  }
4836
5061
 
4837
5062
  V.set(n - 1, n - 1, 1);
@@ -4902,9 +5127,9 @@ function tql2(n, e, d, V) {
4902
5127
  d[i + 1] = h + s * (c * g + s * d[i]);
4903
5128
 
4904
5129
  for (k = 0; k < n; k++) {
4905
- h = V.get(k, i + 1);
4906
- V.set(k, i + 1, s * V.get(k, i) + c * h);
4907
- V.set(k, i, c * V.get(k, i) - s * h);
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);
4908
5133
  }
4909
5134
  }
4910
5135
 
@@ -4931,9 +5156,9 @@ function tql2(n, e, d, V) {
4931
5156
  d[k] = d[i];
4932
5157
  d[i] = p;
4933
5158
  for (j = 0; j < n; j++) {
4934
- p = V.get(j, i);
4935
- V.set(j, i, V.get(j, k));
4936
- V.set(j, k, p);
5159
+ p = V.get(i, j);
5160
+ V.set(i, j, V.get(k, j));
5161
+ V.set(k, j, p);
4937
5162
  }
4938
5163
  }
4939
5164
  }
@@ -5537,7 +5762,7 @@ class nipals {
5537
5762
 
5538
5763
  let u;
5539
5764
  if (Y) {
5540
- if (isAnyArray.isAnyArray(Y) && typeof Y[0] === 'number') {
5765
+ if (isAnyArray(Y) && typeof Y[0] === 'number') {
5541
5766
  Y = Matrix.columnVector(Y);
5542
5767
  } else {
5543
5768
  Y = WrapperMatrix2D.checkMatrix(Y);
@@ -5645,3 +5870,4 @@ exports.linearDependencies = linearDependencies;
5645
5870
  exports.pseudoInverse = pseudoInverse;
5646
5871
  exports.solve = solve;
5647
5872
  exports.wrap = wrap;
5873
+ //# sourceMappingURL=matrix.js.map