UNPKG

43.4 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.createSparseMatrixClass = void 0;
7
8var _is = require("../../utils/is");
9
10var _number = require("../../utils/number");
11
12var _string = require("../../utils/string");
13
14var _object = require("../../utils/object");
15
16var _array = require("../../utils/array");
17
18var _factory = require("../../utils/factory");
19
20var _DimensionError = require("../../error/DimensionError");
21
22var name = 'SparseMatrix';
23var dependencies = ['typed', 'equalScalar', 'Matrix'];
24var createSparseMatrixClass =
25/* #__PURE__ */
26(0, _factory.factory)(name, dependencies, function (_ref) {
27 var typed = _ref.typed,
28 equalScalar = _ref.equalScalar,
29 Matrix = _ref.Matrix;
30
31 /**
32 * Sparse Matrix implementation. This type implements a Compressed Column Storage format
33 * for sparse matrices.
34 * @class SparseMatrix
35 */
36 function SparseMatrix(data, datatype) {
37 if (!(this instanceof SparseMatrix)) {
38 throw new SyntaxError('Constructor must be called with the new operator');
39 }
40
41 if (datatype && !(0, _is.isString)(datatype)) {
42 throw new Error('Invalid datatype: ' + datatype);
43 }
44
45 if ((0, _is.isMatrix)(data)) {
46 // create from matrix
47 _createFromMatrix(this, data, datatype);
48 } else if (data && (0, _is.isArray)(data.index) && (0, _is.isArray)(data.ptr) && (0, _is.isArray)(data.size)) {
49 // initialize fields
50 this._values = data.values;
51 this._index = data.index;
52 this._ptr = data.ptr;
53 this._size = data.size;
54 this._datatype = datatype || data.datatype;
55 } else if ((0, _is.isArray)(data)) {
56 // create from array
57 _createFromArray(this, data, datatype);
58 } else if (data) {
59 // unsupported type
60 throw new TypeError('Unsupported type of data (' + (0, _is.typeOf)(data) + ')');
61 } else {
62 // nothing provided
63 this._values = [];
64 this._index = [];
65 this._ptr = [0];
66 this._size = [0, 0];
67 this._datatype = datatype;
68 }
69 }
70
71 function _createFromMatrix(matrix, source, datatype) {
72 // check matrix type
73 if (source.type === 'SparseMatrix') {
74 // clone arrays
75 matrix._values = source._values ? (0, _object.clone)(source._values) : undefined;
76 matrix._index = (0, _object.clone)(source._index);
77 matrix._ptr = (0, _object.clone)(source._ptr);
78 matrix._size = (0, _object.clone)(source._size);
79 matrix._datatype = datatype || source._datatype;
80 } else {
81 // build from matrix data
82 _createFromArray(matrix, source.valueOf(), datatype || source._datatype);
83 }
84 }
85
86 function _createFromArray(matrix, data, datatype) {
87 // initialize fields
88 matrix._values = [];
89 matrix._index = [];
90 matrix._ptr = [];
91 matrix._datatype = datatype; // discover rows & columns, do not use math.size() to avoid looping array twice
92
93 var rows = data.length;
94 var columns = 0; // equal signature to use
95
96 var eq = equalScalar; // zero value
97
98 var zero = 0;
99
100 if ((0, _is.isString)(datatype)) {
101 // find signature that matches (datatype, datatype)
102 eq = typed.find(equalScalar, [datatype, datatype]) || equalScalar; // convert 0 to the same datatype
103
104 zero = typed.convert(0, datatype);
105 } // check we have rows (empty array)
106
107
108 if (rows > 0) {
109 // column index
110 var j = 0;
111
112 do {
113 // store pointer to values index
114 matrix._ptr.push(matrix._index.length); // loop rows
115
116
117 for (var i = 0; i < rows; i++) {
118 // current row
119 var row = data[i]; // check row is an array
120
121 if ((0, _is.isArray)(row)) {
122 // update columns if needed (only on first column)
123 if (j === 0 && columns < row.length) {
124 columns = row.length;
125 } // check row has column
126
127
128 if (j < row.length) {
129 // value
130 var v = row[j]; // check value != 0
131
132 if (!eq(v, zero)) {
133 // store value
134 matrix._values.push(v); // index
135
136
137 matrix._index.push(i);
138 }
139 }
140 } else {
141 // update columns if needed (only on first column)
142 if (j === 0 && columns < 1) {
143 columns = 1;
144 } // check value != 0 (row is a scalar)
145
146
147 if (!eq(row, zero)) {
148 // store value
149 matrix._values.push(row); // index
150
151
152 matrix._index.push(i);
153 }
154 }
155 } // increment index
156
157
158 j++;
159 } while (j < columns);
160 } // store number of values in ptr
161
162
163 matrix._ptr.push(matrix._index.length); // size
164
165
166 matrix._size = [rows, columns];
167 }
168
169 SparseMatrix.prototype = new Matrix();
170 /**
171 * Create a new SparseMatrix
172 */
173
174 SparseMatrix.prototype.createSparseMatrix = function (data, datatype) {
175 return new SparseMatrix(data, datatype);
176 };
177 /**
178 * Attach type information
179 */
180
181
182 SparseMatrix.prototype.type = 'SparseMatrix';
183 SparseMatrix.prototype.isSparseMatrix = true;
184 /**
185 * Get the matrix type
186 *
187 * Usage:
188 * const matrixType = matrix.getDataType() // retrieves the matrix type
189 *
190 * @memberOf SparseMatrix
191 * @return {string} type information; if multiple types are found from the Matrix, it will return "mixed"
192 */
193
194 SparseMatrix.prototype.getDataType = function () {
195 return (0, _array.getArrayDataType)(this._values, _is.typeOf);
196 };
197 /**
198 * Get the storage format used by the matrix.
199 *
200 * Usage:
201 * const format = matrix.storage() // retrieve storage format
202 *
203 * @memberof SparseMatrix
204 * @return {string} The storage format.
205 */
206
207
208 SparseMatrix.prototype.storage = function () {
209 return 'sparse';
210 };
211 /**
212 * Get the datatype of the data stored in the matrix.
213 *
214 * Usage:
215 * const format = matrix.datatype() // retrieve matrix datatype
216 *
217 * @memberof SparseMatrix
218 * @return {string} The datatype.
219 */
220
221
222 SparseMatrix.prototype.datatype = function () {
223 return this._datatype;
224 };
225 /**
226 * Create a new SparseMatrix
227 * @memberof SparseMatrix
228 * @param {Array} data
229 * @param {string} [datatype]
230 */
231
232
233 SparseMatrix.prototype.create = function (data, datatype) {
234 return new SparseMatrix(data, datatype);
235 };
236 /**
237 * Get the matrix density.
238 *
239 * Usage:
240 * const density = matrix.density() // retrieve matrix density
241 *
242 * @memberof SparseMatrix
243 * @return {number} The matrix density.
244 */
245
246
247 SparseMatrix.prototype.density = function () {
248 // rows & columns
249 var rows = this._size[0];
250 var columns = this._size[1]; // calculate density
251
252 return rows !== 0 && columns !== 0 ? this._index.length / (rows * columns) : 0;
253 };
254 /**
255 * Get a subset of the matrix, or replace a subset of the matrix.
256 *
257 * Usage:
258 * const subset = matrix.subset(index) // retrieve subset
259 * const value = matrix.subset(index, replacement) // replace subset
260 *
261 * @memberof SparseMatrix
262 * @param {Index} index
263 * @param {Array | Matrix | *} [replacement]
264 * @param {*} [defaultValue=0] Default value, filled in on new entries when
265 * the matrix is resized. If not provided,
266 * new matrix elements will be filled with zeros.
267 */
268
269
270 SparseMatrix.prototype.subset = function (index, replacement, defaultValue) {
271 // check it is a pattern matrix
272 if (!this._values) {
273 throw new Error('Cannot invoke subset on a Pattern only matrix');
274 } // check arguments
275
276
277 switch (arguments.length) {
278 case 1:
279 return _getsubset(this, index);
280 // intentional fall through
281
282 case 2:
283 case 3:
284 return _setsubset(this, index, replacement, defaultValue);
285
286 default:
287 throw new SyntaxError('Wrong number of arguments');
288 }
289 };
290
291 function _getsubset(matrix, idx) {
292 // check idx
293 if (!(0, _is.isIndex)(idx)) {
294 throw new TypeError('Invalid index');
295 }
296
297 var isScalar = idx.isScalar();
298
299 if (isScalar) {
300 // return a scalar
301 return matrix.get(idx.min());
302 } // validate dimensions
303
304
305 var size = idx.size();
306
307 if (size.length !== matrix._size.length) {
308 throw new _DimensionError.DimensionError(size.length, matrix._size.length);
309 } // vars
310
311
312 var i, ii, k, kk; // validate if any of the ranges in the index is out of range
313
314 var min = idx.min();
315 var max = idx.max();
316
317 for (i = 0, ii = matrix._size.length; i < ii; i++) {
318 (0, _array.validateIndex)(min[i], matrix._size[i]);
319 (0, _array.validateIndex)(max[i], matrix._size[i]);
320 } // matrix arrays
321
322
323 var mvalues = matrix._values;
324 var mindex = matrix._index;
325 var mptr = matrix._ptr; // rows & columns dimensions for result matrix
326
327 var rows = idx.dimension(0);
328 var columns = idx.dimension(1); // workspace & permutation vector
329
330 var w = [];
331 var pv = []; // loop rows in resulting matrix
332
333 rows.forEach(function (i, r) {
334 // update permutation vector
335 pv[i] = r[0]; // mark i in workspace
336
337 w[i] = true;
338 }); // result matrix arrays
339
340 var values = mvalues ? [] : undefined;
341 var index = [];
342 var ptr = []; // loop columns in result matrix
343
344 columns.forEach(function (j) {
345 // update ptr
346 ptr.push(index.length); // loop values in column j
347
348 for (k = mptr[j], kk = mptr[j + 1]; k < kk; k++) {
349 // row
350 i = mindex[k]; // check row is in result matrix
351
352 if (w[i] === true) {
353 // push index
354 index.push(pv[i]); // check we need to process values
355
356 if (values) {
357 values.push(mvalues[k]);
358 }
359 }
360 }
361 }); // update ptr
362
363 ptr.push(index.length); // return matrix
364
365 return new SparseMatrix({
366 values: values,
367 index: index,
368 ptr: ptr,
369 size: size,
370 datatype: matrix._datatype
371 });
372 }
373
374 function _setsubset(matrix, index, submatrix, defaultValue) {
375 // check index
376 if (!index || index.isIndex !== true) {
377 throw new TypeError('Invalid index');
378 } // get index size and check whether the index contains a single value
379
380
381 var iSize = index.size();
382 var isScalar = index.isScalar(); // calculate the size of the submatrix, and convert it into an Array if needed
383
384 var sSize;
385
386 if ((0, _is.isMatrix)(submatrix)) {
387 // submatrix size
388 sSize = submatrix.size(); // use array representation
389
390 submatrix = submatrix.toArray();
391 } else {
392 // get submatrix size (array, scalar)
393 sSize = (0, _array.arraySize)(submatrix);
394 } // check index is a scalar
395
396
397 if (isScalar) {
398 // verify submatrix is a scalar
399 if (sSize.length !== 0) {
400 throw new TypeError('Scalar expected');
401 } // set value
402
403
404 matrix.set(index.min(), submatrix, defaultValue);
405 } else {
406 // validate dimensions, index size must be one or two dimensions
407 if (iSize.length !== 1 && iSize.length !== 2) {
408 throw new _DimensionError.DimensionError(iSize.length, matrix._size.length, '<');
409 } // check submatrix and index have the same dimensions
410
411
412 if (sSize.length < iSize.length) {
413 // calculate number of missing outer dimensions
414 var i = 0;
415 var outer = 0;
416
417 while (iSize[i] === 1 && sSize[i] === 1) {
418 i++;
419 }
420
421 while (iSize[i] === 1) {
422 outer++;
423 i++;
424 } // unsqueeze both outer and inner dimensions
425
426
427 submatrix = (0, _array.unsqueeze)(submatrix, iSize.length, outer, sSize);
428 } // check whether the size of the submatrix matches the index size
429
430
431 if (!(0, _object.deepStrictEqual)(iSize, sSize)) {
432 throw new _DimensionError.DimensionError(iSize, sSize, '>');
433 } // offsets
434
435
436 var x0 = index.min()[0];
437 var y0 = index.min()[1]; // submatrix rows and columns
438
439 var m = sSize[0];
440 var n = sSize[1]; // loop submatrix
441
442 for (var x = 0; x < m; x++) {
443 // loop columns
444 for (var y = 0; y < n; y++) {
445 // value at i, j
446 var v = submatrix[x][y]; // invoke set (zero value will remove entry from matrix)
447
448 matrix.set([x + x0, y + y0], v, defaultValue);
449 }
450 }
451 }
452
453 return matrix;
454 }
455 /**
456 * Get a single element from the matrix.
457 * @memberof SparseMatrix
458 * @param {number[]} index Zero-based index
459 * @return {*} value
460 */
461
462
463 SparseMatrix.prototype.get = function (index) {
464 if (!(0, _is.isArray)(index)) {
465 throw new TypeError('Array expected');
466 }
467
468 if (index.length !== this._size.length) {
469 throw new _DimensionError.DimensionError(index.length, this._size.length);
470 } // check it is a pattern matrix
471
472
473 if (!this._values) {
474 throw new Error('Cannot invoke get on a Pattern only matrix');
475 } // row and column
476
477
478 var i = index[0];
479 var j = index[1]; // check i, j are valid
480
481 (0, _array.validateIndex)(i, this._size[0]);
482 (0, _array.validateIndex)(j, this._size[1]); // find value index
483
484 var k = _getValueIndex(i, this._ptr[j], this._ptr[j + 1], this._index); // check k is prior to next column k and it is in the correct row
485
486
487 if (k < this._ptr[j + 1] && this._index[k] === i) {
488 return this._values[k];
489 }
490
491 return 0;
492 };
493 /**
494 * Replace a single element in the matrix.
495 * @memberof SparseMatrix
496 * @param {number[]} index Zero-based index
497 * @param {*} v
498 * @param {*} [defaultValue] Default value, filled in on new entries when
499 * the matrix is resized. If not provided,
500 * new matrix elements will be set to zero.
501 * @return {SparseMatrix} self
502 */
503
504
505 SparseMatrix.prototype.set = function (index, v, defaultValue) {
506 if (!(0, _is.isArray)(index)) {
507 throw new TypeError('Array expected');
508 }
509
510 if (index.length !== this._size.length) {
511 throw new _DimensionError.DimensionError(index.length, this._size.length);
512 } // check it is a pattern matrix
513
514
515 if (!this._values) {
516 throw new Error('Cannot invoke set on a Pattern only matrix');
517 } // row and column
518
519
520 var i = index[0];
521 var j = index[1]; // rows & columns
522
523 var rows = this._size[0];
524 var columns = this._size[1]; // equal signature to use
525
526 var eq = equalScalar; // zero value
527
528 var zero = 0;
529
530 if ((0, _is.isString)(this._datatype)) {
531 // find signature that matches (datatype, datatype)
532 eq = typed.find(equalScalar, [this._datatype, this._datatype]) || equalScalar; // convert 0 to the same datatype
533
534 zero = typed.convert(0, this._datatype);
535 } // check we need to resize matrix
536
537
538 if (i > rows - 1 || j > columns - 1) {
539 // resize matrix
540 _resize(this, Math.max(i + 1, rows), Math.max(j + 1, columns), defaultValue); // update rows & columns
541
542
543 rows = this._size[0];
544 columns = this._size[1];
545 } // check i, j are valid
546
547
548 (0, _array.validateIndex)(i, rows);
549 (0, _array.validateIndex)(j, columns); // find value index
550
551 var k = _getValueIndex(i, this._ptr[j], this._ptr[j + 1], this._index); // check k is prior to next column k and it is in the correct row
552
553
554 if (k < this._ptr[j + 1] && this._index[k] === i) {
555 // check value != 0
556 if (!eq(v, zero)) {
557 // update value
558 this._values[k] = v;
559 } else {
560 // remove value from matrix
561 _remove(k, j, this._values, this._index, this._ptr);
562 }
563 } else {
564 // insert value @ (i, j)
565 _insert(k, i, j, v, this._values, this._index, this._ptr);
566 }
567
568 return this;
569 };
570
571 function _getValueIndex(i, top, bottom, index) {
572 // check row is on the bottom side
573 if (bottom - top === 0) {
574 return bottom;
575 } // loop rows [top, bottom[
576
577
578 for (var r = top; r < bottom; r++) {
579 // check we found value index
580 if (index[r] === i) {
581 return r;
582 }
583 } // we did not find row
584
585
586 return top;
587 }
588
589 function _remove(k, j, values, index, ptr) {
590 // remove value @ k
591 values.splice(k, 1);
592 index.splice(k, 1); // update pointers
593
594 for (var x = j + 1; x < ptr.length; x++) {
595 ptr[x]--;
596 }
597 }
598
599 function _insert(k, i, j, v, values, index, ptr) {
600 // insert value
601 values.splice(k, 0, v); // update row for k
602
603 index.splice(k, 0, i); // update column pointers
604
605 for (var x = j + 1; x < ptr.length; x++) {
606 ptr[x]++;
607 }
608 }
609 /**
610 * Resize the matrix to the given size. Returns a copy of the matrix when
611 * `copy=true`, otherwise return the matrix itself (resize in place).
612 *
613 * @memberof SparseMatrix
614 * @param {number[]} size The new size the matrix should have.
615 * @param {*} [defaultValue=0] Default value, filled in on new entries.
616 * If not provided, the matrix elements will
617 * be filled with zeros.
618 * @param {boolean} [copy] Return a resized copy of the matrix
619 *
620 * @return {Matrix} The resized matrix
621 */
622
623
624 SparseMatrix.prototype.resize = function (size, defaultValue, copy) {
625 // validate arguments
626 if (!(0, _is.isArray)(size)) {
627 throw new TypeError('Array expected');
628 }
629
630 if (size.length !== 2) {
631 throw new Error('Only two dimensions matrix are supported');
632 } // check sizes
633
634
635 size.forEach(function (value) {
636 if (!(0, _is.isNumber)(value) || !(0, _number.isInteger)(value) || value < 0) {
637 throw new TypeError('Invalid size, must contain positive integers ' + '(size: ' + (0, _string.format)(size) + ')');
638 }
639 }); // matrix to resize
640
641 var m = copy ? this.clone() : this; // resize matrix
642
643 return _resize(m, size[0], size[1], defaultValue);
644 };
645
646 function _resize(matrix, rows, columns, defaultValue) {
647 // value to insert at the time of growing matrix
648 var value = defaultValue || 0; // equal signature to use
649
650 var eq = equalScalar; // zero value
651
652 var zero = 0;
653
654 if ((0, _is.isString)(matrix._datatype)) {
655 // find signature that matches (datatype, datatype)
656 eq = typed.find(equalScalar, [matrix._datatype, matrix._datatype]) || equalScalar; // convert 0 to the same datatype
657
658 zero = typed.convert(0, matrix._datatype); // convert value to the same datatype
659
660 value = typed.convert(value, matrix._datatype);
661 } // should we insert the value?
662
663
664 var ins = !eq(value, zero); // old columns and rows
665
666 var r = matrix._size[0];
667 var c = matrix._size[1];
668 var i, j, k; // check we need to increase columns
669
670 if (columns > c) {
671 // loop new columns
672 for (j = c; j < columns; j++) {
673 // update matrix._ptr for current column
674 matrix._ptr[j] = matrix._values.length; // check we need to insert matrix._values
675
676 if (ins) {
677 // loop rows
678 for (i = 0; i < r; i++) {
679 // add new matrix._values
680 matrix._values.push(value); // update matrix._index
681
682
683 matrix._index.push(i);
684 }
685 }
686 } // store number of matrix._values in matrix._ptr
687
688
689 matrix._ptr[columns] = matrix._values.length;
690 } else if (columns < c) {
691 // truncate matrix._ptr
692 matrix._ptr.splice(columns + 1, c - columns); // truncate matrix._values and matrix._index
693
694
695 matrix._values.splice(matrix._ptr[columns], matrix._values.length);
696
697 matrix._index.splice(matrix._ptr[columns], matrix._index.length);
698 } // update columns
699
700
701 c = columns; // check we need to increase rows
702
703 if (rows > r) {
704 // check we have to insert values
705 if (ins) {
706 // inserts
707 var n = 0; // loop columns
708
709 for (j = 0; j < c; j++) {
710 // update matrix._ptr for current column
711 matrix._ptr[j] = matrix._ptr[j] + n; // where to insert matrix._values
712
713 k = matrix._ptr[j + 1] + n; // pointer
714
715 var p = 0; // loop new rows, initialize pointer
716
717 for (i = r; i < rows; i++, p++) {
718 // add value
719 matrix._values.splice(k + p, 0, value); // update matrix._index
720
721
722 matrix._index.splice(k + p, 0, i); // increment inserts
723
724
725 n++;
726 }
727 } // store number of matrix._values in matrix._ptr
728
729
730 matrix._ptr[c] = matrix._values.length;
731 }
732 } else if (rows < r) {
733 // deletes
734 var d = 0; // loop columns
735
736 for (j = 0; j < c; j++) {
737 // update matrix._ptr for current column
738 matrix._ptr[j] = matrix._ptr[j] - d; // where matrix._values start for next column
739
740 var k0 = matrix._ptr[j];
741 var k1 = matrix._ptr[j + 1] - d; // loop matrix._index
742
743 for (k = k0; k < k1; k++) {
744 // row
745 i = matrix._index[k]; // check we need to delete value and matrix._index
746
747 if (i > rows - 1) {
748 // remove value
749 matrix._values.splice(k, 1); // remove item from matrix._index
750
751
752 matrix._index.splice(k, 1); // increase deletes
753
754
755 d++;
756 }
757 }
758 } // update matrix._ptr for current column
759
760
761 matrix._ptr[j] = matrix._values.length;
762 } // update matrix._size
763
764
765 matrix._size[0] = rows;
766 matrix._size[1] = columns; // return matrix
767
768 return matrix;
769 }
770 /**
771 * Reshape the matrix to the given size. Returns a copy of the matrix when
772 * `copy=true`, otherwise return the matrix itself (reshape in place).
773 *
774 * NOTE: This might be better suited to copy by default, instead of modifying
775 * in place. For now, it operates in place to remain consistent with
776 * resize().
777 *
778 * @memberof SparseMatrix
779 * @param {number[]} size The new size the matrix should have.
780 * @param {boolean} [copy] Return a reshaped copy of the matrix
781 *
782 * @return {Matrix} The reshaped matrix
783 */
784
785
786 SparseMatrix.prototype.reshape = function (size, copy) {
787 // validate arguments
788 if (!(0, _is.isArray)(size)) {
789 throw new TypeError('Array expected');
790 }
791
792 if (size.length !== 2) {
793 throw new Error('Sparse matrices can only be reshaped in two dimensions');
794 } // check sizes
795
796
797 size.forEach(function (value) {
798 if (!(0, _is.isNumber)(value) || !(0, _number.isInteger)(value) || value < 0) {
799 throw new TypeError('Invalid size, must contain positive integers ' + '(size: ' + (0, _string.format)(size) + ')');
800 }
801 }); // m * n must not change
802
803 if (this._size[0] * this._size[1] !== size[0] * size[1]) {
804 throw new Error('Reshaping sparse matrix will result in the wrong number of elements');
805 } // matrix to reshape
806
807
808 var m = copy ? this.clone() : this; // return unchanged if the same shape
809
810 if (this._size[0] === size[0] && this._size[1] === size[1]) {
811 return m;
812 } // Convert to COO format (generate a column index)
813
814
815 var colIndex = [];
816
817 for (var i = 0; i < m._ptr.length; i++) {
818 for (var j = 0; j < m._ptr[i + 1] - m._ptr[i]; j++) {
819 colIndex.push(i);
820 }
821 } // Clone the values array
822
823
824 var values = m._values.slice(); // Clone the row index array
825
826
827 var rowIndex = m._index.slice(); // Transform the (row, column) indices
828
829
830 for (var _i = 0; _i < m._index.length; _i++) {
831 var r1 = rowIndex[_i];
832 var c1 = colIndex[_i];
833 var flat = r1 * m._size[1] + c1;
834 colIndex[_i] = flat % size[1];
835 rowIndex[_i] = Math.floor(flat / size[1]);
836 } // Now reshaping is supposed to preserve the row-major order, BUT these sparse matrices are stored
837 // in column-major order, so we have to reorder the value array now. One option is to use a multisort,
838 // sorting several arrays based on some other array.
839 // OR, we could easily just:
840 // 1. Remove all values from the matrix
841
842
843 m._values.length = 0;
844 m._index.length = 0;
845 m._ptr.length = size[1] + 1;
846 m._size = size.slice();
847
848 for (var _i2 = 0; _i2 < m._ptr.length; _i2++) {
849 m._ptr[_i2] = 0;
850 } // 2. Re-insert all elements in the proper order (simplified code from SparseMatrix.prototype.set)
851 // This step is probably the most time-consuming
852
853
854 for (var h = 0; h < values.length; h++) {
855 var _i3 = rowIndex[h];
856 var _j = colIndex[h];
857 var v = values[h];
858
859 var k = _getValueIndex(_i3, m._ptr[_j], m._ptr[_j + 1], m._index);
860
861 _insert(k, _i3, _j, v, m._values, m._index, m._ptr);
862 } // The value indices are inserted out of order, but apparently that's... still OK?
863
864
865 return m;
866 };
867 /**
868 * Create a clone of the matrix
869 * @memberof SparseMatrix
870 * @return {SparseMatrix} clone
871 */
872
873
874 SparseMatrix.prototype.clone = function () {
875 var m = new SparseMatrix({
876 values: this._values ? (0, _object.clone)(this._values) : undefined,
877 index: (0, _object.clone)(this._index),
878 ptr: (0, _object.clone)(this._ptr),
879 size: (0, _object.clone)(this._size),
880 datatype: this._datatype
881 });
882 return m;
883 };
884 /**
885 * Retrieve the size of the matrix.
886 * @memberof SparseMatrix
887 * @returns {number[]} size
888 */
889
890
891 SparseMatrix.prototype.size = function () {
892 return this._size.slice(0); // copy the Array
893 };
894 /**
895 * Create a new matrix with the results of the callback function executed on
896 * each entry of the matrix.
897 * @memberof SparseMatrix
898 * @param {Function} callback The callback function is invoked with three
899 * parameters: the value of the element, the index
900 * of the element, and the Matrix being traversed.
901 * @param {boolean} [skipZeros] Invoke callback function for non-zero values only.
902 *
903 * @return {SparseMatrix} matrix
904 */
905
906
907 SparseMatrix.prototype.map = function (callback, skipZeros) {
908 // check it is a pattern matrix
909 if (!this._values) {
910 throw new Error('Cannot invoke map on a Pattern only matrix');
911 } // matrix instance
912
913
914 var me = this; // rows and columns
915
916 var rows = this._size[0];
917 var columns = this._size[1]; // invoke callback
918
919 var invoke = function invoke(v, i, j) {
920 // invoke callback
921 return callback(v, [i, j], me);
922 }; // invoke _map
923
924
925 return _map(this, 0, rows - 1, 0, columns - 1, invoke, skipZeros);
926 };
927 /**
928 * Create a new matrix with the results of the callback function executed on the interval
929 * [minRow..maxRow, minColumn..maxColumn].
930 */
931
932
933 function _map(matrix, minRow, maxRow, minColumn, maxColumn, callback, skipZeros) {
934 // result arrays
935 var values = [];
936 var index = [];
937 var ptr = []; // equal signature to use
938
939 var eq = equalScalar; // zero value
940
941 var zero = 0;
942
943 if ((0, _is.isString)(matrix._datatype)) {
944 // find signature that matches (datatype, datatype)
945 eq = typed.find(equalScalar, [matrix._datatype, matrix._datatype]) || equalScalar; // convert 0 to the same datatype
946
947 zero = typed.convert(0, matrix._datatype);
948 } // invoke callback
949
950
951 var invoke = function invoke(v, x, y) {
952 // invoke callback
953 v = callback(v, x, y); // check value != 0
954
955 if (!eq(v, zero)) {
956 // store value
957 values.push(v); // index
958
959 index.push(x);
960 }
961 }; // loop columns
962
963
964 for (var j = minColumn; j <= maxColumn; j++) {
965 // store pointer to values index
966 ptr.push(values.length); // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1]
967
968 var k0 = matrix._ptr[j];
969 var k1 = matrix._ptr[j + 1];
970
971 if (skipZeros) {
972 // loop k within [k0, k1[
973 for (var k = k0; k < k1; k++) {
974 // row index
975 var i = matrix._index[k]; // check i is in range
976
977 if (i >= minRow && i <= maxRow) {
978 // value @ k
979 invoke(matrix._values[k], i - minRow, j - minColumn);
980 }
981 }
982 } else {
983 // create a cache holding all defined values
984 var _values = {};
985
986 for (var _k = k0; _k < k1; _k++) {
987 var _i4 = matrix._index[_k];
988 _values[_i4] = matrix._values[_k];
989 } // loop over all rows (indexes can be unordered so we can't use that),
990 // and either read the value or zero
991
992
993 for (var _i5 = minRow; _i5 <= maxRow; _i5++) {
994 var value = _i5 in _values ? _values[_i5] : 0;
995 invoke(value, _i5 - minRow, j - minColumn);
996 }
997 }
998 } // store number of values in ptr
999
1000
1001 ptr.push(values.length); // return sparse matrix
1002
1003 return new SparseMatrix({
1004 values: values,
1005 index: index,
1006 ptr: ptr,
1007 size: [maxRow - minRow + 1, maxColumn - minColumn + 1]
1008 });
1009 }
1010 /**
1011 * Execute a callback function on each entry of the matrix.
1012 * @memberof SparseMatrix
1013 * @param {Function} callback The callback function is invoked with three
1014 * parameters: the value of the element, the index
1015 * of the element, and the Matrix being traversed.
1016 * @param {boolean} [skipZeros] Invoke callback function for non-zero values only.
1017 */
1018
1019
1020 SparseMatrix.prototype.forEach = function (callback, skipZeros) {
1021 // check it is a pattern matrix
1022 if (!this._values) {
1023 throw new Error('Cannot invoke forEach on a Pattern only matrix');
1024 } // matrix instance
1025
1026
1027 var me = this; // rows and columns
1028
1029 var rows = this._size[0];
1030 var columns = this._size[1]; // loop columns
1031
1032 for (var j = 0; j < columns; j++) {
1033 // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1]
1034 var k0 = this._ptr[j];
1035 var k1 = this._ptr[j + 1];
1036
1037 if (skipZeros) {
1038 // loop k within [k0, k1[
1039 for (var k = k0; k < k1; k++) {
1040 // row index
1041 var i = this._index[k]; // value @ k
1042
1043 callback(this._values[k], [i, j], me);
1044 }
1045 } else {
1046 // create a cache holding all defined values
1047 var values = {};
1048
1049 for (var _k2 = k0; _k2 < k1; _k2++) {
1050 var _i6 = this._index[_k2];
1051 values[_i6] = this._values[_k2];
1052 } // loop over all rows (indexes can be unordered so we can't use that),
1053 // and either read the value or zero
1054
1055
1056 for (var _i7 = 0; _i7 < rows; _i7++) {
1057 var value = _i7 in values ? values[_i7] : 0;
1058 callback(value, [_i7, j], me);
1059 }
1060 }
1061 }
1062 };
1063 /**
1064 * Create an Array with a copy of the data of the SparseMatrix
1065 * @memberof SparseMatrix
1066 * @returns {Array} array
1067 */
1068
1069
1070 SparseMatrix.prototype.toArray = function () {
1071 return _toArray(this._values, this._index, this._ptr, this._size, true);
1072 };
1073 /**
1074 * Get the primitive value of the SparseMatrix: a two dimensions array
1075 * @memberof SparseMatrix
1076 * @returns {Array} array
1077 */
1078
1079
1080 SparseMatrix.prototype.valueOf = function () {
1081 return _toArray(this._values, this._index, this._ptr, this._size, false);
1082 };
1083
1084 function _toArray(values, index, ptr, size, copy) {
1085 // rows and columns
1086 var rows = size[0];
1087 var columns = size[1]; // result
1088
1089 var a = []; // vars
1090
1091 var i, j; // initialize array
1092
1093 for (i = 0; i < rows; i++) {
1094 a[i] = [];
1095
1096 for (j = 0; j < columns; j++) {
1097 a[i][j] = 0;
1098 }
1099 } // loop columns
1100
1101
1102 for (j = 0; j < columns; j++) {
1103 // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1]
1104 var k0 = ptr[j];
1105 var k1 = ptr[j + 1]; // loop k within [k0, k1[
1106
1107 for (var k = k0; k < k1; k++) {
1108 // row index
1109 i = index[k]; // set value (use one for pattern matrix)
1110
1111 a[i][j] = values ? copy ? (0, _object.clone)(values[k]) : values[k] : 1;
1112 }
1113 }
1114
1115 return a;
1116 }
1117 /**
1118 * Get a string representation of the matrix, with optional formatting options.
1119 * @memberof SparseMatrix
1120 * @param {Object | number | Function} [options] Formatting options. See
1121 * lib/utils/number:format for a
1122 * description of the available
1123 * options.
1124 * @returns {string} str
1125 */
1126
1127
1128 SparseMatrix.prototype.format = function (options) {
1129 // rows and columns
1130 var rows = this._size[0];
1131 var columns = this._size[1]; // density
1132
1133 var density = this.density(); // rows & columns
1134
1135 var str = 'Sparse Matrix [' + (0, _string.format)(rows, options) + ' x ' + (0, _string.format)(columns, options) + '] density: ' + (0, _string.format)(density, options) + '\n'; // loop columns
1136
1137 for (var j = 0; j < columns; j++) {
1138 // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1]
1139 var k0 = this._ptr[j];
1140 var k1 = this._ptr[j + 1]; // loop k within [k0, k1[
1141
1142 for (var k = k0; k < k1; k++) {
1143 // row index
1144 var i = this._index[k]; // append value
1145
1146 str += '\n (' + (0, _string.format)(i, options) + ', ' + (0, _string.format)(j, options) + ') ==> ' + (this._values ? (0, _string.format)(this._values[k], options) : 'X');
1147 }
1148 }
1149
1150 return str;
1151 };
1152 /**
1153 * Get a string representation of the matrix
1154 * @memberof SparseMatrix
1155 * @returns {string} str
1156 */
1157
1158
1159 SparseMatrix.prototype.toString = function () {
1160 return (0, _string.format)(this.toArray());
1161 };
1162 /**
1163 * Get a JSON representation of the matrix
1164 * @memberof SparseMatrix
1165 * @returns {Object}
1166 */
1167
1168
1169 SparseMatrix.prototype.toJSON = function () {
1170 return {
1171 mathjs: 'SparseMatrix',
1172 values: this._values,
1173 index: this._index,
1174 ptr: this._ptr,
1175 size: this._size,
1176 datatype: this._datatype
1177 };
1178 };
1179 /**
1180 * Get the kth Matrix diagonal.
1181 *
1182 * @memberof SparseMatrix
1183 * @param {number | BigNumber} [k=0] The kth diagonal where the vector will retrieved.
1184 *
1185 * @returns {Matrix} The matrix vector with the diagonal values.
1186 */
1187
1188
1189 SparseMatrix.prototype.diagonal = function (k) {
1190 // validate k if any
1191 if (k) {
1192 // convert BigNumber to a number
1193 if ((0, _is.isBigNumber)(k)) {
1194 k = k.toNumber();
1195 } // is must be an integer
1196
1197
1198 if (!(0, _is.isNumber)(k) || !(0, _number.isInteger)(k)) {
1199 throw new TypeError('The parameter k must be an integer number');
1200 }
1201 } else {
1202 // default value
1203 k = 0;
1204 }
1205
1206 var kSuper = k > 0 ? k : 0;
1207 var kSub = k < 0 ? -k : 0; // rows & columns
1208
1209 var rows = this._size[0];
1210 var columns = this._size[1]; // number diagonal values
1211
1212 var n = Math.min(rows - kSub, columns - kSuper); // diagonal arrays
1213
1214 var values = [];
1215 var index = [];
1216 var ptr = []; // initial ptr value
1217
1218 ptr[0] = 0; // loop columns
1219
1220 for (var j = kSuper; j < columns && values.length < n; j++) {
1221 // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1]
1222 var k0 = this._ptr[j];
1223 var k1 = this._ptr[j + 1]; // loop x within [k0, k1[
1224
1225 for (var x = k0; x < k1; x++) {
1226 // row index
1227 var i = this._index[x]; // check row
1228
1229 if (i === j - kSuper + kSub) {
1230 // value on this column
1231 values.push(this._values[x]); // store row
1232
1233 index[values.length - 1] = i - kSub; // exit loop
1234
1235 break;
1236 }
1237 }
1238 } // close ptr
1239
1240
1241 ptr.push(values.length); // return matrix
1242
1243 return new SparseMatrix({
1244 values: values,
1245 index: index,
1246 ptr: ptr,
1247 size: [n, 1]
1248 });
1249 };
1250 /**
1251 * Generate a matrix from a JSON object
1252 * @memberof SparseMatrix
1253 * @param {Object} json An object structured like
1254 * `{"mathjs": "SparseMatrix", "values": [], "index": [], "ptr": [], "size": []}`,
1255 * where mathjs is optional
1256 * @returns {SparseMatrix}
1257 */
1258
1259
1260 SparseMatrix.fromJSON = function (json) {
1261 return new SparseMatrix(json);
1262 };
1263 /**
1264 * Create a diagonal matrix.
1265 *
1266 * @memberof SparseMatrix
1267 * @param {Array} size The matrix size.
1268 * @param {number | Array | Matrix } value The values for the diagonal.
1269 * @param {number | BigNumber} [k=0] The kth diagonal where the vector will be filled in.
1270 * @param {number} [defaultValue] The default value for non-diagonal
1271 * @param {string} [datatype] The Matrix datatype, values must be of this datatype.
1272 *
1273 * @returns {SparseMatrix}
1274 */
1275
1276
1277 SparseMatrix.diagonal = function (size, value, k, defaultValue, datatype) {
1278 if (!(0, _is.isArray)(size)) {
1279 throw new TypeError('Array expected, size parameter');
1280 }
1281
1282 if (size.length !== 2) {
1283 throw new Error('Only two dimensions matrix are supported');
1284 } // map size & validate
1285
1286
1287 size = size.map(function (s) {
1288 // check it is a big number
1289 if ((0, _is.isBigNumber)(s)) {
1290 // convert it
1291 s = s.toNumber();
1292 } // validate arguments
1293
1294
1295 if (!(0, _is.isNumber)(s) || !(0, _number.isInteger)(s) || s < 1) {
1296 throw new Error('Size values must be positive integers');
1297 }
1298
1299 return s;
1300 }); // validate k if any
1301
1302 if (k) {
1303 // convert BigNumber to a number
1304 if ((0, _is.isBigNumber)(k)) {
1305 k = k.toNumber();
1306 } // is must be an integer
1307
1308
1309 if (!(0, _is.isNumber)(k) || !(0, _number.isInteger)(k)) {
1310 throw new TypeError('The parameter k must be an integer number');
1311 }
1312 } else {
1313 // default value
1314 k = 0;
1315 } // equal signature to use
1316
1317
1318 var eq = equalScalar; // zero value
1319
1320 var zero = 0;
1321
1322 if ((0, _is.isString)(datatype)) {
1323 // find signature that matches (datatype, datatype)
1324 eq = typed.find(equalScalar, [datatype, datatype]) || equalScalar; // convert 0 to the same datatype
1325
1326 zero = typed.convert(0, datatype);
1327 }
1328
1329 var kSuper = k > 0 ? k : 0;
1330 var kSub = k < 0 ? -k : 0; // rows and columns
1331
1332 var rows = size[0];
1333 var columns = size[1]; // number of non-zero items
1334
1335 var n = Math.min(rows - kSub, columns - kSuper); // value extraction function
1336
1337 var _value; // check value
1338
1339
1340 if ((0, _is.isArray)(value)) {
1341 // validate array
1342 if (value.length !== n) {
1343 // number of values in array must be n
1344 throw new Error('Invalid value array length');
1345 } // define function
1346
1347
1348 _value = function _value(i) {
1349 // return value @ i
1350 return value[i];
1351 };
1352 } else if ((0, _is.isMatrix)(value)) {
1353 // matrix size
1354 var ms = value.size(); // validate matrix
1355
1356 if (ms.length !== 1 || ms[0] !== n) {
1357 // number of values in array must be n
1358 throw new Error('Invalid matrix length');
1359 } // define function
1360
1361
1362 _value = function _value(i) {
1363 // return value @ i
1364 return value.get([i]);
1365 };
1366 } else {
1367 // define function
1368 _value = function _value() {
1369 // return value
1370 return value;
1371 };
1372 } // create arrays
1373
1374
1375 var values = [];
1376 var index = [];
1377 var ptr = []; // loop items
1378
1379 for (var j = 0; j < columns; j++) {
1380 // number of rows with value
1381 ptr.push(values.length); // diagonal index
1382
1383 var i = j - kSuper; // check we need to set diagonal value
1384
1385 if (i >= 0 && i < n) {
1386 // get value @ i
1387 var v = _value(i); // check for zero
1388
1389
1390 if (!eq(v, zero)) {
1391 // column
1392 index.push(i + kSub); // add value
1393
1394 values.push(v);
1395 }
1396 }
1397 } // last value should be number of values
1398
1399
1400 ptr.push(values.length); // create SparseMatrix
1401
1402 return new SparseMatrix({
1403 values: values,
1404 index: index,
1405 ptr: ptr,
1406 size: [rows, columns]
1407 });
1408 };
1409 /**
1410 * Swap rows i and j in Matrix.
1411 *
1412 * @memberof SparseMatrix
1413 * @param {number} i Matrix row index 1
1414 * @param {number} j Matrix row index 2
1415 *
1416 * @return {Matrix} The matrix reference
1417 */
1418
1419
1420 SparseMatrix.prototype.swapRows = function (i, j) {
1421 // check index
1422 if (!(0, _is.isNumber)(i) || !(0, _number.isInteger)(i) || !(0, _is.isNumber)(j) || !(0, _number.isInteger)(j)) {
1423 throw new Error('Row index must be positive integers');
1424 } // check dimensions
1425
1426
1427 if (this._size.length !== 2) {
1428 throw new Error('Only two dimensional matrix is supported');
1429 } // validate index
1430
1431
1432 (0, _array.validateIndex)(i, this._size[0]);
1433 (0, _array.validateIndex)(j, this._size[0]); // swap rows
1434
1435 SparseMatrix._swapRows(i, j, this._size[1], this._values, this._index, this._ptr); // return current instance
1436
1437
1438 return this;
1439 };
1440 /**
1441 * Loop rows with data in column j.
1442 *
1443 * @param {number} j Column
1444 * @param {Array} values Matrix values
1445 * @param {Array} index Matrix row indeces
1446 * @param {Array} ptr Matrix column pointers
1447 * @param {Function} callback Callback function invoked for every row in column j
1448 */
1449
1450
1451 SparseMatrix._forEachRow = function (j, values, index, ptr, callback) {
1452 // indeces for column j
1453 var k0 = ptr[j];
1454 var k1 = ptr[j + 1]; // loop
1455
1456 for (var k = k0; k < k1; k++) {
1457 // invoke callback
1458 callback(index[k], values[k]);
1459 }
1460 };
1461 /**
1462 * Swap rows x and y in Sparse Matrix data structures.
1463 *
1464 * @param {number} x Matrix row index 1
1465 * @param {number} y Matrix row index 2
1466 * @param {number} columns Number of columns in matrix
1467 * @param {Array} values Matrix values
1468 * @param {Array} index Matrix row indeces
1469 * @param {Array} ptr Matrix column pointers
1470 */
1471
1472
1473 SparseMatrix._swapRows = function (x, y, columns, values, index, ptr) {
1474 // loop columns
1475 for (var j = 0; j < columns; j++) {
1476 // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1]
1477 var k0 = ptr[j];
1478 var k1 = ptr[j + 1]; // find value index @ x
1479
1480 var kx = _getValueIndex(x, k0, k1, index); // find value index @ x
1481
1482
1483 var ky = _getValueIndex(y, k0, k1, index); // check both rows exist in matrix
1484
1485
1486 if (kx < k1 && ky < k1 && index[kx] === x && index[ky] === y) {
1487 // swap values (check for pattern matrix)
1488 if (values) {
1489 var v = values[kx];
1490 values[kx] = values[ky];
1491 values[ky] = v;
1492 } // next column
1493
1494
1495 continue;
1496 } // check x row exist & no y row
1497
1498
1499 if (kx < k1 && index[kx] === x && (ky >= k1 || index[ky] !== y)) {
1500 // value @ x (check for pattern matrix)
1501 var vx = values ? values[kx] : undefined; // insert value @ y
1502
1503 index.splice(ky, 0, y);
1504
1505 if (values) {
1506 values.splice(ky, 0, vx);
1507 } // remove value @ x (adjust array index if needed)
1508
1509
1510 index.splice(ky <= kx ? kx + 1 : kx, 1);
1511
1512 if (values) {
1513 values.splice(ky <= kx ? kx + 1 : kx, 1);
1514 } // next column
1515
1516
1517 continue;
1518 } // check y row exist & no x row
1519
1520
1521 if (ky < k1 && index[ky] === y && (kx >= k1 || index[kx] !== x)) {
1522 // value @ y (check for pattern matrix)
1523 var vy = values ? values[ky] : undefined; // insert value @ x
1524
1525 index.splice(kx, 0, x);
1526
1527 if (values) {
1528 values.splice(kx, 0, vy);
1529 } // remove value @ y (adjust array index if needed)
1530
1531
1532 index.splice(kx <= ky ? ky + 1 : ky, 1);
1533
1534 if (values) {
1535 values.splice(kx <= ky ? ky + 1 : ky, 1);
1536 }
1537 }
1538 }
1539 };
1540
1541 return SparseMatrix;
1542}, {
1543 isClass: true
1544});
1545exports.createSparseMatrixClass = createSparseMatrixClass;
\No newline at end of file