ml-matrix 6.10.8 → 6.11.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
@@ -1472,6 +1472,16 @@ class AbstractMatrix {
1472
1472
  return false;
1473
1473
  }
1474
1474
 
1475
+ isDistance() {
1476
+ if (!this.isSymmetric()) return false;
1477
+
1478
+ for (let i = 0; i < this.rows; i++) {
1479
+ if (this.get(i, i) !== 0) return false;
1480
+ }
1481
+
1482
+ return true;
1483
+ }
1484
+
1475
1485
  isEchelonForm() {
1476
1486
  let i = 0;
1477
1487
  let j = 0;
@@ -2546,13 +2556,21 @@ class AbstractMatrix {
2546
2556
  }
2547
2557
 
2548
2558
  clone() {
2549
- let newMatrix = new Matrix(this.rows, this.columns);
2550
- for (let row = 0; row < this.rows; row++) {
2551
- for (let column = 0; column < this.columns; column++) {
2552
- newMatrix.set(row, column, this.get(row, column));
2553
- }
2559
+ return this.constructor.copy(this, new Matrix(this.rows, this.columns));
2560
+ }
2561
+
2562
+ /**
2563
+ * @template {AbstractMatrix} M
2564
+ * @param {AbstractMatrix} from
2565
+ * @param {M} to
2566
+ * @return {M}
2567
+ */
2568
+ static copy(from, to) {
2569
+ for (const [row, column, value] of from.entries()) {
2570
+ to.set(row, column, value);
2554
2571
  }
2555
- return newMatrix;
2572
+
2573
+ return to;
2556
2574
  }
2557
2575
 
2558
2576
  sum(by) {
@@ -2736,6 +2754,36 @@ class AbstractMatrix {
2736
2754
  toString(options) {
2737
2755
  return inspectMatrixWithOptions(this, options);
2738
2756
  }
2757
+
2758
+ [Symbol.iterator]() {
2759
+ return this.entries();
2760
+ }
2761
+
2762
+ /**
2763
+ * iterator from left to right, from top to bottom
2764
+ * yield [row, column, value]
2765
+ * @returns {Generator<[number, number, number], void, *>}
2766
+ */
2767
+ *entries() {
2768
+ for (let row = 0; row < this.rows; row++) {
2769
+ for (let col = 0; col < this.columns; col++) {
2770
+ yield [row, col, this.get(row, col)];
2771
+ }
2772
+ }
2773
+ }
2774
+
2775
+ /**
2776
+ * iterator from left to right, from top to bottom
2777
+ * yield value
2778
+ * @returns {Generator<number, void, *>}
2779
+ */
2780
+ *values() {
2781
+ for (let row = 0; row < this.rows; row++) {
2782
+ for (let col = 0; col < this.columns; col++) {
2783
+ yield this.get(row, col);
2784
+ }
2785
+ }
2786
+ }
2739
2787
  }
2740
2788
 
2741
2789
  AbstractMatrix.prototype.klass = 'Matrix';
@@ -2765,21 +2813,38 @@ AbstractMatrix.prototype.tensorProduct =
2765
2813
  AbstractMatrix.prototype.kroneckerProduct;
2766
2814
 
2767
2815
  class Matrix extends AbstractMatrix {
2816
+ /**
2817
+ * @type {Float64Array[]}
2818
+ */
2819
+ data;
2820
+
2821
+ /**
2822
+ * Init an empty matrix
2823
+ * @param {number} nRows
2824
+ * @param {number} nColumns
2825
+ */
2826
+ #initData(nRows, nColumns) {
2827
+ this.data = [];
2828
+
2829
+ if (Number.isInteger(nColumns) && nColumns >= 0) {
2830
+ for (let i = 0; i < nRows; i++) {
2831
+ this.data.push(new Float64Array(nColumns));
2832
+ }
2833
+ } else {
2834
+ throw new TypeError('nColumns must be a positive integer');
2835
+ }
2836
+
2837
+ this.rows = nRows;
2838
+ this.columns = nColumns;
2839
+ }
2840
+
2768
2841
  constructor(nRows, nColumns) {
2769
2842
  super();
2770
2843
  if (Matrix.isMatrix(nRows)) {
2771
- // eslint-disable-next-line no-constructor-return
2772
- return nRows.clone();
2844
+ this.#initData(nRows.rows, nRows.columns);
2845
+ Matrix.copy(nRows, this);
2773
2846
  } else if (Number.isInteger(nRows) && nRows >= 0) {
2774
- // Create an empty matrix
2775
- this.data = [];
2776
- if (Number.isInteger(nColumns) && nColumns >= 0) {
2777
- for (let i = 0; i < nRows; i++) {
2778
- this.data.push(new Float64Array(nColumns));
2779
- }
2780
- } else {
2781
- throw new TypeError('nColumns must be a positive integer');
2782
- }
2847
+ this.#initData(nRows, nColumns);
2783
2848
  } else if (isAnyArray.isAnyArray(nRows)) {
2784
2849
  // Copy the values from the 2D array
2785
2850
  const arrayData = nRows;
@@ -2791,6 +2856,7 @@ class Matrix extends AbstractMatrix {
2791
2856
  );
2792
2857
  }
2793
2858
  this.data = [];
2859
+
2794
2860
  for (let i = 0; i < nRows; i++) {
2795
2861
  if (arrayData[i].length !== nColumns) {
2796
2862
  throw new RangeError('Inconsistent array dimensions');
@@ -2800,13 +2866,14 @@ class Matrix extends AbstractMatrix {
2800
2866
  }
2801
2867
  this.data.push(Float64Array.from(arrayData[i]));
2802
2868
  }
2869
+
2870
+ this.rows = nRows;
2871
+ this.columns = nColumns;
2803
2872
  } else {
2804
2873
  throw new TypeError(
2805
2874
  'First argument must be a positive number or an array',
2806
2875
  );
2807
2876
  }
2808
- this.rows = nRows;
2809
- this.columns = nColumns;
2810
2877
  }
2811
2878
 
2812
2879
  set(rowIndex, columnIndex, value) {
@@ -2879,6 +2946,374 @@ class Matrix extends AbstractMatrix {
2879
2946
 
2880
2947
  installMathOperations(AbstractMatrix, Matrix);
2881
2948
 
2949
+ /**
2950
+ * @typedef {0 | 1 | number | boolean} Mask
2951
+ */
2952
+
2953
+ class SymmetricMatrix extends AbstractMatrix {
2954
+ /** @type {Matrix} */
2955
+ #matrix;
2956
+
2957
+ get size() {
2958
+ return this.#matrix.size;
2959
+ }
2960
+
2961
+ get rows() {
2962
+ return this.#matrix.rows;
2963
+ }
2964
+
2965
+ get columns() {
2966
+ return this.#matrix.columns;
2967
+ }
2968
+
2969
+ get diagonalSize() {
2970
+ return this.rows;
2971
+ }
2972
+
2973
+ /**
2974
+ * not the same as matrix.isSymmetric()
2975
+ * Here is to check if it's instanceof SymmetricMatrix without bundling issues
2976
+ *
2977
+ * @param value
2978
+ * @returns {boolean}
2979
+ */
2980
+ static isSymmetricMatrix(value) {
2981
+ return Matrix.isMatrix(value) && value.klassType === 'SymmetricMatrix';
2982
+ }
2983
+
2984
+ /**
2985
+ * @param diagonalSize
2986
+ * @return {SymmetricMatrix}
2987
+ */
2988
+ static zeros(diagonalSize) {
2989
+ return new this(diagonalSize);
2990
+ }
2991
+
2992
+ /**
2993
+ * @param diagonalSize
2994
+ * @return {SymmetricMatrix}
2995
+ */
2996
+ static ones(diagonalSize) {
2997
+ return new this(diagonalSize).fill(1);
2998
+ }
2999
+
3000
+ /**
3001
+ * @param {number | AbstractMatrix | ArrayLike<ArrayLike<number>>} diagonalSize
3002
+ * @return {this}
3003
+ */
3004
+ constructor(diagonalSize) {
3005
+ super();
3006
+
3007
+ if (Matrix.isMatrix(diagonalSize)) {
3008
+ if (!diagonalSize.isSymmetric()) {
3009
+ throw new TypeError('not symmetric data');
3010
+ }
3011
+
3012
+ this.#matrix = Matrix.copy(
3013
+ diagonalSize,
3014
+ new Matrix(diagonalSize.rows, diagonalSize.rows),
3015
+ );
3016
+ } else if (Number.isInteger(diagonalSize) && diagonalSize >= 0) {
3017
+ this.#matrix = new Matrix(diagonalSize, diagonalSize);
3018
+ } else {
3019
+ this.#matrix = new Matrix(diagonalSize);
3020
+
3021
+ if (!this.isSymmetric()) {
3022
+ throw new TypeError('not symmetric data');
3023
+ }
3024
+ }
3025
+ }
3026
+
3027
+ clone() {
3028
+ const matrix = new SymmetricMatrix(this.diagonalSize);
3029
+
3030
+ for (const [row, col, value] of this.upperRightEntries()) {
3031
+ matrix.set(row, col, value);
3032
+ }
3033
+
3034
+ return matrix;
3035
+ }
3036
+
3037
+ toMatrix() {
3038
+ return new Matrix(this);
3039
+ }
3040
+
3041
+ get(rowIndex, columnIndex) {
3042
+ return this.#matrix.get(rowIndex, columnIndex);
3043
+ }
3044
+ set(rowIndex, columnIndex, value) {
3045
+ // symmetric set
3046
+ this.#matrix.set(rowIndex, columnIndex, value);
3047
+ this.#matrix.set(columnIndex, rowIndex, value);
3048
+
3049
+ return this;
3050
+ }
3051
+
3052
+ removeCross(index) {
3053
+ // symmetric remove side
3054
+ this.#matrix.removeRow(index);
3055
+ this.#matrix.removeColumn(index);
3056
+
3057
+ return this;
3058
+ }
3059
+
3060
+ addCross(index, array) {
3061
+ if (array === undefined) {
3062
+ array = index;
3063
+ index = this.diagonalSize;
3064
+ }
3065
+
3066
+ const row = array.slice();
3067
+ row.splice(index, 1);
3068
+
3069
+ this.#matrix.addRow(index, row);
3070
+ this.#matrix.addColumn(index, array);
3071
+
3072
+ return this;
3073
+ }
3074
+
3075
+ /**
3076
+ * @param {Mask[]} mask
3077
+ */
3078
+ applyMask(mask) {
3079
+ if (mask.length !== this.diagonalSize) {
3080
+ throw new RangeError('Mask size do not match with matrix size');
3081
+ }
3082
+
3083
+ // prepare sides to remove from matrix from mask
3084
+ /** @type {number[]} */
3085
+ const sidesToRemove = [];
3086
+ for (const [index, passthroughs] of mask.entries()) {
3087
+ if (passthroughs) continue;
3088
+ sidesToRemove.push(index);
3089
+ }
3090
+ // to remove from highest to lowest for no mutation shifting
3091
+ sidesToRemove.reverse();
3092
+
3093
+ // remove sides
3094
+ for (const sideIndex of sidesToRemove) {
3095
+ this.removeCross(sideIndex);
3096
+ }
3097
+
3098
+ return this;
3099
+ }
3100
+
3101
+ /**
3102
+ * Compact format upper-right corner of matrix
3103
+ * iterate from left to right, from top to bottom.
3104
+ *
3105
+ * ```
3106
+ * A B C D
3107
+ * A 1 2 3 4
3108
+ * B 2 5 6 7
3109
+ * C 3 6 8 9
3110
+ * D 4 7 9 10
3111
+ * ```
3112
+ *
3113
+ * will return compact 1D array `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]`
3114
+ *
3115
+ * length is S(i=0, n=sideSize) => 10 for a 4 sideSized matrix
3116
+ *
3117
+ * @returns {number[]}
3118
+ */
3119
+ toCompact() {
3120
+ const { diagonalSize } = this;
3121
+
3122
+ /** @type {number[]} */
3123
+ const compact = new Array((diagonalSize * (diagonalSize + 1)) / 2);
3124
+ for (let col = 0, row = 0, index = 0; index < compact.length; index++) {
3125
+ compact[index] = this.get(row, col);
3126
+
3127
+ if (++col >= diagonalSize) col = ++row;
3128
+ }
3129
+
3130
+ return compact;
3131
+ }
3132
+
3133
+ /**
3134
+ * @param {number[]} compact
3135
+ * @return {SymmetricMatrix}
3136
+ */
3137
+ static fromCompact(compact) {
3138
+ const compactSize = compact.length;
3139
+ // compactSize = (sideSize * (sideSize + 1)) / 2
3140
+ // https://mathsolver.microsoft.com/fr/solve-problem/y%20%3D%20%20x%20%60cdot%20%20%20%60frac%7B%20%20%60left(%20x%2B1%20%20%60right)%20%20%20%20%7D%7B%202%20%20%7D
3141
+ // sideSize = (Sqrt(8 × compactSize + 1) - 1) / 2
3142
+ const diagonalSize = (Math.sqrt(8 * compactSize + 1) - 1) / 2;
3143
+
3144
+ if (!Number.isInteger(diagonalSize)) {
3145
+ throw new TypeError(
3146
+ `This array is not a compact representation of a Symmetric Matrix, ${JSON.stringify(
3147
+ compact,
3148
+ )}`,
3149
+ );
3150
+ }
3151
+
3152
+ const matrix = new SymmetricMatrix(diagonalSize);
3153
+ for (let col = 0, row = 0, index = 0; index < compactSize; index++) {
3154
+ matrix.set(col, row, compact[index]);
3155
+ if (++col >= diagonalSize) col = ++row;
3156
+ }
3157
+
3158
+ return matrix;
3159
+ }
3160
+
3161
+ /**
3162
+ * half iterator upper-right-corner from left to right, from top to bottom
3163
+ * yield [row, column, value]
3164
+ *
3165
+ * @returns {Generator<[number, number, number], void, *>}
3166
+ */
3167
+ *upperRightEntries() {
3168
+ for (let row = 0, col = 0; row < this.diagonalSize; void 0) {
3169
+ const value = this.get(row, col);
3170
+
3171
+ yield [row, col, value];
3172
+
3173
+ // at the end of row, move cursor to next row at diagonal position
3174
+ if (++col >= this.diagonalSize) col = ++row;
3175
+ }
3176
+ }
3177
+
3178
+ /**
3179
+ * half iterator upper-right-corner from left to right, from top to bottom
3180
+ * yield value
3181
+ *
3182
+ * @returns {Generator<[number, number, number], void, *>}
3183
+ */
3184
+ *upperRightValues() {
3185
+ for (let row = 0, col = 0; row < this.diagonalSize; void 0) {
3186
+ const value = this.get(row, col);
3187
+
3188
+ yield value;
3189
+
3190
+ // at the end of row, move cursor to next row at diagonal position
3191
+ if (++col >= this.diagonalSize) col = ++row;
3192
+ }
3193
+ }
3194
+ }
3195
+ SymmetricMatrix.prototype.klassType = 'SymmetricMatrix';
3196
+
3197
+ class DistanceMatrix extends SymmetricMatrix {
3198
+ /**
3199
+ * not the same as matrix.isSymmetric()
3200
+ * Here is to check if it's instanceof SymmetricMatrix without bundling issues
3201
+ *
3202
+ * @param value
3203
+ * @returns {boolean}
3204
+ */
3205
+ static isDistanceMatrix(value) {
3206
+ return (
3207
+ SymmetricMatrix.isSymmetricMatrix(value) &&
3208
+ value.klassSubType === 'DistanceMatrix'
3209
+ );
3210
+ }
3211
+
3212
+ constructor(sideSize) {
3213
+ super(sideSize);
3214
+
3215
+ if (!this.isDistance()) {
3216
+ throw new TypeError('Provided arguments do no produce a distance matrix');
3217
+ }
3218
+ }
3219
+
3220
+ set(rowIndex, columnIndex, value) {
3221
+ // distance matrix diagonal is 0
3222
+ if (rowIndex === columnIndex) value = 0;
3223
+
3224
+ return super.set(rowIndex, columnIndex, value);
3225
+ }
3226
+
3227
+ addCross(index, array) {
3228
+ if (array === undefined) {
3229
+ array = index;
3230
+ index = this.diagonalSize;
3231
+ }
3232
+
3233
+ // ensure distance
3234
+ array = array.slice();
3235
+ array[index] = 0;
3236
+
3237
+ return super.addCross(index, array);
3238
+ }
3239
+
3240
+ toSymmetricMatrix() {
3241
+ return new SymmetricMatrix(this);
3242
+ }
3243
+
3244
+ clone() {
3245
+ const matrix = new DistanceMatrix(this.diagonalSize);
3246
+
3247
+ for (const [row, col, value] of this.upperRightEntries()) {
3248
+ if (row === col) continue;
3249
+ matrix.set(row, col, value);
3250
+ }
3251
+
3252
+ return matrix;
3253
+ }
3254
+
3255
+ /**
3256
+ * Compact format upper-right corner of matrix
3257
+ * no diagonal (only zeros)
3258
+ * iterable from left to right, from top to bottom.
3259
+ *
3260
+ * ```
3261
+ * A B C D
3262
+ * A 0 1 2 3
3263
+ * B 1 0 4 5
3264
+ * C 2 4 0 6
3265
+ * D 3 5 6 0
3266
+ * ```
3267
+ *
3268
+ * will return compact 1D array `[1, 2, 3, 4, 5, 6]`
3269
+ *
3270
+ * length is S(i=0, n=sideSize-1) => 6 for a 4 side sized matrix
3271
+ *
3272
+ * @returns {number[]}
3273
+ */
3274
+ toCompact() {
3275
+ const { diagonalSize } = this;
3276
+ const compactLength = ((diagonalSize - 1) * diagonalSize) / 2;
3277
+
3278
+ /** @type {number[]} */
3279
+ const compact = new Array(compactLength);
3280
+ for (let col = 1, row = 0, index = 0; index < compact.length; index++) {
3281
+ compact[index] = this.get(row, col);
3282
+
3283
+ if (++col >= diagonalSize) col = ++row + 1;
3284
+ }
3285
+
3286
+ return compact;
3287
+ }
3288
+
3289
+ /**
3290
+ * @param {number[]} compact
3291
+ */
3292
+ static fromCompact(compact) {
3293
+ const compactSize = compact.length;
3294
+ // compactSize = (sideSize * (sideSize - 1)) / 2
3295
+ // sideSize = (Sqrt(8 × compactSize + 1) + 1) / 2
3296
+ const diagonalSize = (Math.sqrt(8 * compactSize + 1) + 1) / 2;
3297
+
3298
+ if (!Number.isInteger(diagonalSize)) {
3299
+ throw new TypeError(
3300
+ `This array is not a compact representation of a DistanceMatrix, ${JSON.stringify(
3301
+ compact,
3302
+ )}`,
3303
+ );
3304
+ }
3305
+
3306
+ const matrix = new this(diagonalSize);
3307
+ for (let col = 1, row = 0, index = 0; index < compactSize; index++) {
3308
+ matrix.set(col, row, compact[index]);
3309
+ if (++col >= diagonalSize) col = ++row + 1;
3310
+ }
3311
+
3312
+ return matrix;
3313
+ }
3314
+ }
3315
+ DistanceMatrix.prototype.klassSubType = 'DistanceMatrix';
3316
+
2882
3317
  class BaseView extends AbstractMatrix {
2883
3318
  constructor(matrix, rows, columns) {
2884
3319
  super();
@@ -5147,6 +5582,7 @@ class nipals {
5147
5582
  exports.AbstractMatrix = AbstractMatrix;
5148
5583
  exports.CHO = CholeskyDecomposition;
5149
5584
  exports.CholeskyDecomposition = CholeskyDecomposition;
5585
+ exports.DistanceMatrix = DistanceMatrix;
5150
5586
  exports.EVD = EigenvalueDecomposition;
5151
5587
  exports.EigenvalueDecomposition = EigenvalueDecomposition;
5152
5588
  exports.LU = LuDecomposition;
@@ -5167,6 +5603,7 @@ exports.QR = QrDecomposition;
5167
5603
  exports.QrDecomposition = QrDecomposition;
5168
5604
  exports.SVD = SingularValueDecomposition;
5169
5605
  exports.SingularValueDecomposition = SingularValueDecomposition;
5606
+ exports.SymmetricMatrix = SymmetricMatrix;
5170
5607
  exports.WrapperMatrix1D = WrapperMatrix1D;
5171
5608
  exports.WrapperMatrix2D = WrapperMatrix2D;
5172
5609
  exports.correlation = correlation;
package/matrix.mjs CHANGED
@@ -3,6 +3,7 @@ import * as matrix from './matrix.js';
3
3
  export const AbstractMatrix = matrix.AbstractMatrix;
4
4
  export const CHO = matrix.CHO;
5
5
  export const CholeskyDecomposition = matrix.CholeskyDecomposition;
6
+ export const DistanceMatrix = matrix.DistanceMatrix;
6
7
  export const EVD = matrix.EVD;
7
8
  export const EigenvalueDecomposition = matrix.EigenvalueDecomposition;
8
9
  export const LU = matrix.LU;
@@ -23,6 +24,7 @@ export const QR = matrix.QR;
23
24
  export const QrDecomposition = matrix.QrDecomposition;
24
25
  export const SVD = matrix.SVD;
25
26
  export const SingularValueDecomposition = matrix.SingularValueDecomposition;
27
+ export const SymmetricMatrix = matrix.SymmetricMatrix;
26
28
  export const WrapperMatrix1D = matrix.WrapperMatrix1D;
27
29
  export const WrapperMatrix2D = matrix.WrapperMatrix2D;
28
30
  export const correlation = matrix.correlation;