1 |
|
2 |
|
3 | const util = require('../../utils/index')
|
4 | const DimensionError = require('../../error/DimensionError')
|
5 |
|
6 | const string = util.string
|
7 | const array = util.array
|
8 | const object = util.object
|
9 | const number = util.number
|
10 |
|
11 | const isArray = Array.isArray
|
12 | const isNumber = number.isNumber
|
13 | const isInteger = number.isInteger
|
14 | const isString = string.isString
|
15 |
|
16 | const validateIndex = array.validateIndex
|
17 |
|
18 | function factory (type, config, load, typed) {
|
19 | const getArrayDataType = load(require('./utils/getArrayDataType'))
|
20 | const Matrix = load(require('./Matrix')) // force loading Matrix (do not use via type.Matrix)
|
21 |
|
22 | /**
|
23 | * Dense Matrix implementation. A regular, dense matrix, supporting multi-dimensional matrices. This is the default matrix type.
|
24 | * @class DenseMatrix
|
25 | */
|
26 | function DenseMatrix (data, datatype) {
|
27 | if (!(this instanceof DenseMatrix)) { throw new SyntaxError('Constructor must be called with the new operator') }
|
28 | if (datatype && !isString(datatype)) { throw new Error('Invalid datatype: ' + datatype) }
|
29 |
|
30 | if (type.isMatrix(data)) {
|
31 | // check data is a DenseMatrix
|
32 | if (data.type === 'DenseMatrix') {
|
33 | // clone data & size
|
34 | this._data = object.clone(data._data)
|
35 | this._size = object.clone(data._size)
|
36 | this._datatype = datatype || data._datatype
|
37 | } else {
|
38 | // build data from existing matrix
|
39 | this._data = data.toArray()
|
40 | this._size = data.size()
|
41 | this._datatype = datatype || data._datatype
|
42 | }
|
43 | } else if (data && isArray(data.data) && isArray(data.size)) {
|
44 | // initialize fields from JSON representation
|
45 | this._data = data.data
|
46 | this._size = data.size
|
47 | this._datatype = datatype || data.datatype
|
48 | } else if (isArray(data)) {
|
49 | // replace nested Matrices with Arrays
|
50 | this._data = preprocess(data)
|
51 | // get the dimensions of the array
|
52 | this._size = array.size(this._data)
|
53 | // verify the dimensions of the array, TODO: compute size while processing array
|
54 | array.validate(this._data, this._size)
|
55 | // data type unknown
|
56 | this._datatype = datatype
|
57 | } else if (data) {
|
58 | // unsupported type
|
59 | throw new TypeError('Unsupported type of data (' + util.types.type(data) + ')')
|
60 | } else {
|
61 | // nothing provided
|
62 | this._data = []
|
63 | this._size = [0]
|
64 | this._datatype = datatype
|
65 | }
|
66 | }
|
67 |
|
68 | DenseMatrix.prototype = new Matrix()
|
69 |
|
70 | /**
|
71 | * Attach type information
|
72 | */
|
73 | DenseMatrix.prototype.type = 'DenseMatrix'
|
74 | DenseMatrix.prototype.isDenseMatrix = true
|
75 |
|
76 | /**
|
77 | * Get the matrix type
|
78 | *
|
79 | * Usage:
|
80 | * const matrixType = matrix.getDataType() // retrieves the matrix type
|
81 | *
|
82 | * @memberOf DenseMatrix
|
83 | * @return {string} type information; if multiple types are found from the Matrix, it will return "mixed"
|
84 | */
|
85 | DenseMatrix.prototype.getDataType = function () {
|
86 | return getArrayDataType(this._data)
|
87 | }
|
88 |
|
89 | /**
|
90 | * Get the storage format used by the matrix.
|
91 | *
|
92 | * Usage:
|
93 | * const format = matrix.storage() // retrieve storage format
|
94 | *
|
95 | * @memberof DenseMatrix
|
96 | * @return {string} The storage format.
|
97 | */
|
98 | DenseMatrix.prototype.storage = function () {
|
99 | return 'dense'
|
100 | }
|
101 |
|
102 | /**
|
103 | * Get the datatype of the data stored in the matrix.
|
104 | *
|
105 | * Usage:
|
106 | * const format = matrix.datatype() // retrieve matrix datatype
|
107 | *
|
108 | * @memberof DenseMatrix
|
109 | * @return {string} The datatype.
|
110 | */
|
111 | DenseMatrix.prototype.datatype = function () {
|
112 | return this._datatype
|
113 | }
|
114 |
|
115 | /**
|
116 | * Create a new DenseMatrix
|
117 | * @memberof DenseMatrix
|
118 | * @param {Array} data
|
119 | * @param {string} [datatype]
|
120 | */
|
121 | DenseMatrix.prototype.create = function (data, datatype) {
|
122 | return new DenseMatrix(data, datatype)
|
123 | }
|
124 |
|
125 | /**
|
126 | * Get a subset of the matrix, or replace a subset of the matrix.
|
127 | *
|
128 | * Usage:
|
129 | * const subset = matrix.subset(index) // retrieve subset
|
130 | * const value = matrix.subset(index, replacement) // replace subset
|
131 | *
|
132 | * @memberof DenseMatrix
|
133 | * @param {Index} index
|
134 | * @param {Array | DenseMatrix | *} [replacement]
|
135 | * @param {*} [defaultValue=0] Default value, filled in on new entries when
|
136 | * the matrix is resized. If not provided,
|
137 | * new matrix elements will be filled with zeros.
|
138 | */
|
139 | DenseMatrix.prototype.subset = function (index, replacement, defaultValue) {
|
140 | switch (arguments.length) {
|
141 | case 1:
|
142 | return _get(this, index)
|
143 |
|
144 | // intentional fall through
|
145 | case 2:
|
146 | case 3:
|
147 | return _set(this, index, replacement, defaultValue)
|
148 |
|
149 | default:
|
150 | throw new SyntaxError('Wrong number of arguments')
|
151 | }
|
152 | }
|
153 |
|
154 | /**
|
155 | * Get a single element from the matrix.
|
156 | * @memberof DenseMatrix
|
157 | * @param {number[]} index Zero-based index
|
158 | * @return {*} value
|
159 | */
|
160 | DenseMatrix.prototype.get = function (index) {
|
161 | if (!isArray(index)) { throw new TypeError('Array expected') }
|
162 | if (index.length !== this._size.length) { throw new DimensionError(index.length, this._size.length) }
|
163 |
|
164 | // check index
|
165 | for (let x = 0; x < index.length; x++) { validateIndex(index[x], this._size[x]) }
|
166 |
|
167 | let data = this._data
|
168 | for (let i = 0, ii = index.length; i < ii; i++) {
|
169 | const indexI = index[i]
|
170 | validateIndex(indexI, data.length)
|
171 | data = data[indexI]
|
172 | }
|
173 |
|
174 | return data
|
175 | }
|
176 |
|
177 | /**
|
178 | * Replace a single element in the matrix.
|
179 | * @memberof DenseMatrix
|
180 | * @param {number[]} index Zero-based index
|
181 | * @param {*} value
|
182 | * @param {*} [defaultValue] Default value, filled in on new entries when
|
183 | * the matrix is resized. If not provided,
|
184 | * new matrix elements will be left undefined.
|
185 | * @return {DenseMatrix} self
|
186 | */
|
187 | DenseMatrix.prototype.set = function (index, value, defaultValue) {
|
188 | if (!isArray(index)) { throw new TypeError('Array expected') }
|
189 | if (index.length < this._size.length) { throw new DimensionError(index.length, this._size.length, '<') }
|
190 |
|
191 | let i, ii, indexI
|
192 |
|
193 | // enlarge matrix when needed
|
194 | const size = index.map(function (i) {
|
195 | return i + 1
|
196 | })
|
197 | _fit(this, size, defaultValue)
|
198 |
|
199 | // traverse over the dimensions
|
200 | let data = this._data
|
201 | for (i = 0, ii = index.length - 1; i < ii; i++) {
|
202 | indexI = index[i]
|
203 | validateIndex(indexI, data.length)
|
204 | data = data[indexI]
|
205 | }
|
206 |
|
207 | // set new value
|
208 | indexI = index[index.length - 1]
|
209 | validateIndex(indexI, data.length)
|
210 | data[indexI] = value
|
211 |
|
212 | return this
|
213 | }
|
214 |
|
215 | /**
|
216 | * Get a submatrix of this matrix
|
217 | * @memberof DenseMatrix
|
218 | * @param {DenseMatrix} matrix
|
219 | * @param {Index} index Zero-based index
|
220 | * @private
|
221 | */
|
222 | function _get (matrix, index) {
|
223 | if (!type.isIndex(index)) {
|
224 | throw new TypeError('Invalid index')
|
225 | }
|
226 |
|
227 | const isScalar = index.isScalar()
|
228 | if (isScalar) {
|
229 | // return a scalar
|
230 | return matrix.get(index.min())
|
231 | } else {
|
232 | // validate dimensions
|
233 | const size = index.size()
|
234 | if (size.length !== matrix._size.length) {
|
235 | throw new DimensionError(size.length, matrix._size.length)
|
236 | }
|
237 |
|
238 | // validate if any of the ranges in the index is out of range
|
239 | const min = index.min()
|
240 | const max = index.max()
|
241 | for (let i = 0, ii = matrix._size.length; i < ii; i++) {
|
242 | validateIndex(min[i], matrix._size[i])
|
243 | validateIndex(max[i], matrix._size[i])
|
244 | }
|
245 |
|
246 | // retrieve submatrix
|
247 | // TODO: more efficient when creating an empty matrix and setting _data and _size manually
|
248 | return new DenseMatrix(_getSubmatrix(matrix._data, index, size.length, 0), matrix._datatype)
|
249 | }
|
250 | }
|
251 |
|
252 | /**
|
253 | * Recursively get a submatrix of a multi dimensional matrix.
|
254 | * Index is not checked for correct number or length of dimensions.
|
255 | * @memberof DenseMatrix
|
256 | * @param {Array} data
|
257 | * @param {Index} index
|
258 | * @param {number} dims Total number of dimensions
|
259 | * @param {number} dim Current dimension
|
260 | * @return {Array} submatrix
|
261 | * @private
|
262 | */
|
263 | function _getSubmatrix (data, index, dims, dim) {
|
264 | const last = (dim === dims - 1)
|
265 | const range = index.dimension(dim)
|
266 |
|
267 | if (last) {
|
268 | return range.map(function (i) {
|
269 | validateIndex(i, data.length)
|
270 | return data[i]
|
271 | }).valueOf()
|
272 | } else {
|
273 | return range.map(function (i) {
|
274 | validateIndex(i, data.length)
|
275 | const child = data[i]
|
276 | return _getSubmatrix(child, index, dims, dim + 1)
|
277 | }).valueOf()
|
278 | }
|
279 | }
|
280 |
|
281 | /**
|
282 | * Replace a submatrix in this matrix
|
283 | * Indexes are zero-based.
|
284 | * @memberof DenseMatrix
|
285 | * @param {DenseMatrix} matrix
|
286 | * @param {Index} index
|
287 | * @param {DenseMatrix | Array | *} submatrix
|
288 | * @param {*} defaultValue Default value, filled in on new entries when
|
289 | * the matrix is resized.
|
290 | * @return {DenseMatrix} matrix
|
291 | * @private
|
292 | */
|
293 | function _set (matrix, index, submatrix, defaultValue) {
|
294 | if (!index || index.isIndex !== true) {
|
295 | throw new TypeError('Invalid index')
|
296 | }
|
297 |
|
298 | // get index size and check whether the index contains a single value
|
299 | const iSize = index.size()
|
300 | const isScalar = index.isScalar()
|
301 |
|
302 | // calculate the size of the submatrix, and convert it into an Array if needed
|
303 | let sSize
|
304 | if (type.isMatrix(submatrix)) {
|
305 | sSize = submatrix.size()
|
306 | submatrix = submatrix.valueOf()
|
307 | } else {
|
308 | sSize = array.size(submatrix)
|
309 | }
|
310 |
|
311 | if (isScalar) {
|
312 | // set a scalar
|
313 |
|
314 | // check whether submatrix is a scalar
|
315 | if (sSize.length !== 0) {
|
316 | throw new TypeError('Scalar expected')
|
317 | }
|
318 |
|
319 | matrix.set(index.min(), submatrix, defaultValue)
|
320 | } else {
|
321 | // set a submatrix
|
322 |
|
323 | // validate dimensions
|
324 | if (iSize.length < matrix._size.length) {
|
325 | throw new DimensionError(iSize.length, matrix._size.length, '<')
|
326 | }
|
327 |
|
328 | if (sSize.length < iSize.length) {
|
329 | // calculate number of missing outer dimensions
|
330 | let i = 0
|
331 | let outer = 0
|
332 | while (iSize[i] === 1 && sSize[i] === 1) {
|
333 | i++
|
334 | }
|
335 | while (iSize[i] === 1) {
|
336 | outer++
|
337 | i++
|
338 | }
|
339 |
|
340 | // unsqueeze both outer and inner dimensions
|
341 | submatrix = array.unsqueeze(submatrix, iSize.length, outer, sSize)
|
342 | }
|
343 |
|
344 | // check whether the size of the submatrix matches the index size
|
345 | if (!object.deepEqual(iSize, sSize)) {
|
346 | throw new DimensionError(iSize, sSize, '>')
|
347 | }
|
348 |
|
349 | // enlarge matrix when needed
|
350 | const size = index.max().map(function (i) {
|
351 | return i + 1
|
352 | })
|
353 | _fit(matrix, size, defaultValue)
|
354 |
|
355 | // insert the sub matrix
|
356 | const dims = iSize.length
|
357 | const dim = 0
|
358 | _setSubmatrix(matrix._data, index, submatrix, dims, dim)
|
359 | }
|
360 |
|
361 | return matrix
|
362 | }
|
363 |
|
364 | /**
|
365 | * Replace a submatrix of a multi dimensional matrix.
|
366 | * @memberof DenseMatrix
|
367 | * @param {Array} data
|
368 | * @param {Index} index
|
369 | * @param {Array} submatrix
|
370 | * @param {number} dims Total number of dimensions
|
371 | * @param {number} dim
|
372 | * @private
|
373 | */
|
374 | function _setSubmatrix (data, index, submatrix, dims, dim) {
|
375 | const last = (dim === dims - 1)
|
376 | const range = index.dimension(dim)
|
377 |
|
378 | if (last) {
|
379 | range.forEach(function (dataIndex, subIndex) {
|
380 | validateIndex(dataIndex)
|
381 | data[dataIndex] = submatrix[subIndex[0]]
|
382 | })
|
383 | } else {
|
384 | range.forEach(function (dataIndex, subIndex) {
|
385 | validateIndex(dataIndex)
|
386 | _setSubmatrix(data[dataIndex], index, submatrix[subIndex[0]], dims, dim + 1)
|
387 | })
|
388 | }
|
389 | }
|
390 |
|
391 | /**
|
392 | * Resize the matrix to the given size. Returns a copy of the matrix when
|
393 | * `copy=true`, otherwise return the matrix itself (resize in place).
|
394 | *
|
395 | * @memberof DenseMatrix
|
396 | * @param {number[]} size The new size the matrix should have.
|
397 | * @param {*} [defaultValue=0] Default value, filled in on new entries.
|
398 | * If not provided, the matrix elements will
|
399 | * be filled with zeros.
|
400 | * @param {boolean} [copy] Return a resized copy of the matrix
|
401 | *
|
402 | * @return {Matrix} The resized matrix
|
403 | */
|
404 | DenseMatrix.prototype.resize = function (size, defaultValue, copy) {
|
405 | // validate arguments
|
406 | if (!isArray(size)) { throw new TypeError('Array expected') }
|
407 |
|
408 | // matrix to resize
|
409 | const m = copy ? this.clone() : this
|
410 | // resize matrix
|
411 | return _resize(m, size, defaultValue)
|
412 | }
|
413 |
|
414 | function _resize (matrix, size, defaultValue) {
|
415 | // check size
|
416 | if (size.length === 0) {
|
417 | // first value in matrix
|
418 | let v = matrix._data
|
419 | // go deep
|
420 | while (isArray(v)) {
|
421 | v = v[0]
|
422 | }
|
423 | return v
|
424 | }
|
425 | // resize matrix
|
426 | matrix._size = size.slice(0) // copy the array
|
427 | matrix._data = array.resize(matrix._data, matrix._size, defaultValue)
|
428 | // return matrix
|
429 | return matrix
|
430 | }
|
431 |
|
432 | /**
|
433 | * Reshape the matrix to the given size. Returns a copy of the matrix when
|
434 | * `copy=true`, otherwise return the matrix itself (reshape in place).
|
435 | *
|
436 | * NOTE: This might be better suited to copy by default, instead of modifying
|
437 | * in place. For now, it operates in place to remain consistent with
|
438 | * resize().
|
439 | *
|
440 | * @memberof DenseMatrix
|
441 | * @param {number[]} size The new size the matrix should have.
|
442 | * @param {boolean} [copy] Return a reshaped copy of the matrix
|
443 | *
|
444 | * @return {Matrix} The reshaped matrix
|
445 | */
|
446 | DenseMatrix.prototype.reshape = function (size, copy) {
|
447 | const m = copy ? this.clone() : this
|
448 |
|
449 | m._data = array.reshape(m._data, size)
|
450 | m._size = size.slice(0)
|
451 | return m
|
452 | }
|
453 |
|
454 | /**
|
455 | * Enlarge the matrix when it is smaller than given size.
|
456 | * If the matrix is larger or equal sized, nothing is done.
|
457 | * @memberof DenseMatrix
|
458 | * @param {DenseMatrix} matrix The matrix to be resized
|
459 | * @param {number[]} size
|
460 | * @param {*} defaultValue Default value, filled in on new entries.
|
461 | * @private
|
462 | */
|
463 | function _fit (matrix, size, defaultValue) {
|
464 | const // copy the array
|
465 | newSize = matrix._size.slice(0)
|
466 |
|
467 | let changed = false
|
468 |
|
469 | // add dimensions when needed
|
470 | while (newSize.length < size.length) {
|
471 | newSize.push(0)
|
472 | changed = true
|
473 | }
|
474 |
|
475 | // enlarge size when needed
|
476 | for (let i = 0, ii = size.length; i < ii; i++) {
|
477 | if (size[i] > newSize[i]) {
|
478 | newSize[i] = size[i]
|
479 | changed = true
|
480 | }
|
481 | }
|
482 |
|
483 | if (changed) {
|
484 | // resize only when size is changed
|
485 | _resize(matrix, newSize, defaultValue)
|
486 | }
|
487 | }
|
488 |
|
489 | /**
|
490 | * Create a clone of the matrix
|
491 | * @memberof DenseMatrix
|
492 | * @return {DenseMatrix} clone
|
493 | */
|
494 | DenseMatrix.prototype.clone = function () {
|
495 | const m = new DenseMatrix({
|
496 | data: object.clone(this._data),
|
497 | size: object.clone(this._size),
|
498 | datatype: this._datatype
|
499 | })
|
500 | return m
|
501 | }
|
502 |
|
503 | /**
|
504 | * Retrieve the size of the matrix.
|
505 | * @memberof DenseMatrix
|
506 | * @returns {number[]} size
|
507 | */
|
508 | DenseMatrix.prototype.size = function () {
|
509 | return this._size.slice(0) // return a clone of _size
|
510 | }
|
511 |
|
512 | /**
|
513 | * Create a new matrix with the results of the callback function executed on
|
514 | * each entry of the matrix.
|
515 | * @memberof DenseMatrix
|
516 | * @param {Function} callback The callback function is invoked with three
|
517 | * parameters: the value of the element, the index
|
518 | * of the element, and the Matrix being traversed.
|
519 | *
|
520 | * @return {DenseMatrix} matrix
|
521 | */
|
522 | DenseMatrix.prototype.map = function (callback) {
|
523 | // matrix instance
|
524 | const me = this
|
525 | const recurse = function (value, index) {
|
526 | if (isArray(value)) {
|
527 | return value.map(function (child, i) {
|
528 | return recurse(child, index.concat(i))
|
529 | })
|
530 | } else {
|
531 | return callback(value, index, me)
|
532 | }
|
533 | }
|
534 | // return dense format
|
535 | return new DenseMatrix({
|
536 | data: recurse(this._data, []),
|
537 | size: object.clone(this._size),
|
538 | datatype: this._datatype
|
539 | })
|
540 | }
|
541 |
|
542 | /**
|
543 | * Execute a callback function on each entry of the matrix.
|
544 | * @memberof DenseMatrix
|
545 | * @param {Function} callback The callback function is invoked with three
|
546 | * parameters: the value of the element, the index
|
547 | * of the element, and the Matrix being traversed.
|
548 | */
|
549 | DenseMatrix.prototype.forEach = function (callback) {
|
550 | // matrix instance
|
551 | const me = this
|
552 | const recurse = function (value, index) {
|
553 | if (isArray(value)) {
|
554 | value.forEach(function (child, i) {
|
555 | recurse(child, index.concat(i))
|
556 | })
|
557 | } else {
|
558 | callback(value, index, me)
|
559 | }
|
560 | }
|
561 | recurse(this._data, [])
|
562 | }
|
563 |
|
564 | /**
|
565 | * Create an Array with a copy of the data of the DenseMatrix
|
566 | * @memberof DenseMatrix
|
567 | * @returns {Array} array
|
568 | */
|
569 | DenseMatrix.prototype.toArray = function () {
|
570 | return object.clone(this._data)
|
571 | }
|
572 |
|
573 | /**
|
574 | * Get the primitive value of the DenseMatrix: a multidimensional array
|
575 | * @memberof DenseMatrix
|
576 | * @returns {Array} array
|
577 | */
|
578 | DenseMatrix.prototype.valueOf = function () {
|
579 | return this._data
|
580 | }
|
581 |
|
582 | /**
|
583 | * Get a string representation of the matrix, with optional formatting options.
|
584 | * @memberof DenseMatrix
|
585 | * @param {Object | number | Function} [options] Formatting options. See
|
586 | * lib/utils/number:format for a
|
587 | * description of the available
|
588 | * options.
|
589 | * @returns {string} str
|
590 | */
|
591 | DenseMatrix.prototype.format = function (options) {
|
592 | return string.format(this._data, options)
|
593 | }
|
594 |
|
595 | /**
|
596 | * Get a string representation of the matrix
|
597 | * @memberof DenseMatrix
|
598 | * @returns {string} str
|
599 | */
|
600 | DenseMatrix.prototype.toString = function () {
|
601 | return string.format(this._data)
|
602 | }
|
603 |
|
604 | /**
|
605 | * Get a JSON representation of the matrix
|
606 | * @memberof DenseMatrix
|
607 | * @returns {Object}
|
608 | */
|
609 | DenseMatrix.prototype.toJSON = function () {
|
610 | return {
|
611 | mathjs: 'DenseMatrix',
|
612 | data: this._data,
|
613 | size: this._size,
|
614 | datatype: this._datatype
|
615 | }
|
616 | }
|
617 |
|
618 | /**
|
619 | * Get the kth Matrix diagonal.
|
620 | *
|
621 | * @memberof DenseMatrix
|
622 | * @param {number | BigNumber} [k=0] The kth diagonal where the vector will retrieved.
|
623 | *
|
624 | * @returns {Array} The array vector with the diagonal values.
|
625 | */
|
626 | DenseMatrix.prototype.diagonal = function (k) {
|
627 | // validate k if any
|
628 | if (k) {
|
629 | // convert BigNumber to a number
|
630 | if (type.isBigNumber(k)) { k = k.toNumber() }
|
631 | // is must be an integer
|
632 | if (!isNumber(k) || !isInteger(k)) {
|
633 | throw new TypeError('The parameter k must be an integer number')
|
634 | }
|
635 | } else {
|
636 | // default value
|
637 | k = 0
|
638 | }
|
639 |
|
640 | const kSuper = k > 0 ? k : 0
|
641 | const kSub = k < 0 ? -k : 0
|
642 |
|
643 | // rows & columns
|
644 | const rows = this._size[0]
|
645 | const columns = this._size[1]
|
646 |
|
647 | // number diagonal values
|
648 | const n = Math.min(rows - kSub, columns - kSuper)
|
649 |
|
650 | // x is a matrix get diagonal from matrix
|
651 | const data = []
|
652 |
|
653 | // loop rows
|
654 | for (let i = 0; i < n; i++) {
|
655 | data[i] = this._data[i + kSub][i + kSuper]
|
656 | }
|
657 |
|
658 | // create DenseMatrix
|
659 | return new DenseMatrix({
|
660 | data: data,
|
661 | size: [n],
|
662 | datatype: this._datatype
|
663 | })
|
664 | }
|
665 |
|
666 | /**
|
667 | * Create a diagonal matrix.
|
668 | *
|
669 | * @memberof DenseMatrix
|
670 | * @param {Array} size The matrix size.
|
671 | * @param {number | Array} value The values for the diagonal.
|
672 | * @param {number | BigNumber} [k=0] The kth diagonal where the vector will be filled in.
|
673 | * @param {number} [defaultValue] The default value for non-diagonal
|
674 | *
|
675 | * @returns {DenseMatrix}
|
676 | */
|
677 | DenseMatrix.diagonal = function (size, value, k, defaultValue, datatype) {
|
678 | if (!isArray(size)) { throw new TypeError('Array expected, size parameter') }
|
679 | if (size.length !== 2) { throw new Error('Only two dimensions matrix are supported') }
|
680 |
|
681 | // map size & validate
|
682 | size = size.map(function (s) {
|
683 | // check it is a big number
|
684 | if (type.isBigNumber(s)) {
|
685 | // convert it
|
686 | s = s.toNumber()
|
687 | }
|
688 | // validate arguments
|
689 | if (!isNumber(s) || !isInteger(s) || s < 1) {
|
690 | throw new Error('Size values must be positive integers')
|
691 | }
|
692 | return s
|
693 | })
|
694 |
|
695 | // validate k if any
|
696 | if (k) {
|
697 | // convert BigNumber to a number
|
698 | if (type.isBigNumber(k)) { k = k.toNumber() }
|
699 | // is must be an integer
|
700 | if (!isNumber(k) || !isInteger(k)) {
|
701 | throw new TypeError('The parameter k must be an integer number')
|
702 | }
|
703 | } else {
|
704 | // default value
|
705 | k = 0
|
706 | }
|
707 |
|
708 | if (defaultValue && isString(datatype)) {
|
709 | // convert defaultValue to the same datatype
|
710 | defaultValue = typed.convert(defaultValue, datatype)
|
711 | }
|
712 |
|
713 | const kSuper = k > 0 ? k : 0
|
714 | const kSub = k < 0 ? -k : 0
|
715 |
|
716 | // rows and columns
|
717 | const rows = size[0]
|
718 | const columns = size[1]
|
719 |
|
720 | // number of non-zero items
|
721 | const n = Math.min(rows - kSub, columns - kSuper)
|
722 |
|
723 | // value extraction function
|
724 | let _value
|
725 |
|
726 | // check value
|
727 | if (isArray(value)) {
|
728 | // validate array
|
729 | if (value.length !== n) {
|
730 | // number of values in array must be n
|
731 | throw new Error('Invalid value array length')
|
732 | }
|
733 | // define function
|
734 | _value = function (i) {
|
735 | // return value @ i
|
736 | return value[i]
|
737 | }
|
738 | } else if (type.isMatrix(value)) {
|
739 | // matrix size
|
740 | const ms = value.size()
|
741 | // validate matrix
|
742 | if (ms.length !== 1 || ms[0] !== n) {
|
743 | // number of values in array must be n
|
744 | throw new Error('Invalid matrix length')
|
745 | }
|
746 | // define function
|
747 | _value = function (i) {
|
748 | // return value @ i
|
749 | return value.get([i])
|
750 | }
|
751 | } else {
|
752 | // define function
|
753 | _value = function () {
|
754 | // return value
|
755 | return value
|
756 | }
|
757 | }
|
758 |
|
759 | // discover default value if needed
|
760 | if (!defaultValue) {
|
761 | // check first value in array
|
762 | defaultValue = type.isBigNumber(_value(0)) ? new type.BigNumber(0) : 0
|
763 | }
|
764 |
|
765 | // empty array
|
766 | let data = []
|
767 |
|
768 | // check we need to resize array
|
769 | if (size.length > 0) {
|
770 | // resize array
|
771 | data = array.resize(data, size, defaultValue)
|
772 | // fill diagonal
|
773 | for (let d = 0; d < n; d++) {
|
774 | data[d + kSub][d + kSuper] = _value(d)
|
775 | }
|
776 | }
|
777 |
|
778 | // create DenseMatrix
|
779 | return new DenseMatrix({
|
780 | data: data,
|
781 | size: [rows, columns]
|
782 | })
|
783 | }
|
784 |
|
785 | /**
|
786 | * Generate a matrix from a JSON object
|
787 | * @memberof DenseMatrix
|
788 | * @param {Object} json An object structured like
|
789 | * `{"mathjs": "DenseMatrix", data: [], size: []}`,
|
790 | * where mathjs is optional
|
791 | * @returns {DenseMatrix}
|
792 | */
|
793 | DenseMatrix.fromJSON = function (json) {
|
794 | return new DenseMatrix(json)
|
795 | }
|
796 |
|
797 | /**
|
798 | * Swap rows i and j in Matrix.
|
799 | *
|
800 | * @memberof DenseMatrix
|
801 | * @param {number} i Matrix row index 1
|
802 | * @param {number} j Matrix row index 2
|
803 | *
|
804 | * @return {Matrix} The matrix reference
|
805 | */
|
806 | DenseMatrix.prototype.swapRows = function (i, j) {
|
807 | // check index
|
808 | if (!isNumber(i) || !isInteger(i) || !isNumber(j) || !isInteger(j)) {
|
809 | throw new Error('Row index must be positive integers')
|
810 | }
|
811 | // check dimensions
|
812 | if (this._size.length !== 2) {
|
813 | throw new Error('Only two dimensional matrix is supported')
|
814 | }
|
815 | // validate index
|
816 | validateIndex(i, this._size[0])
|
817 | validateIndex(j, this._size[0])
|
818 |
|
819 | // swap rows
|
820 | DenseMatrix._swapRows(i, j, this._data)
|
821 | // return current instance
|
822 | return this
|
823 | }
|
824 |
|
825 | /**
|
826 | * Swap rows i and j in Dense Matrix data structure.
|
827 | *
|
828 | * @param {number} i Matrix row index 1
|
829 | * @param {number} j Matrix row index 2
|
830 | */
|
831 | DenseMatrix._swapRows = function (i, j, data) {
|
832 | // swap values i <-> j
|
833 | const vi = data[i]
|
834 | data[i] = data[j]
|
835 | data[j] = vi
|
836 | }
|
837 |
|
838 | /**
|
839 | * Preprocess data, which can be an Array or DenseMatrix with nested Arrays and
|
840 | * Matrices. Replaces all nested Matrices with Arrays
|
841 | * @memberof DenseMatrix
|
842 | * @param {Array} data
|
843 | * @return {Array} data
|
844 | */
|
845 | function preprocess (data) {
|
846 | for (let i = 0, ii = data.length; i < ii; i++) {
|
847 | const elem = data[i]
|
848 | if (isArray(elem)) {
|
849 | data[i] = preprocess(elem)
|
850 | } else if (elem && elem.isMatrix === true) {
|
851 | data[i] = preprocess(elem.valueOf())
|
852 | }
|
853 | }
|
854 |
|
855 | return data
|
856 | }
|
857 |
|
858 | // register this type in the base class Matrix
|
859 | type.Matrix._storage.dense = DenseMatrix
|
860 | type.Matrix._storage['default'] = DenseMatrix
|
861 |
|
862 | // exports
|
863 | return DenseMatrix
|
864 | }
|
865 |
|
866 | exports.name = 'DenseMatrix'
|
867 | exports.path = 'type'
|
868 | exports.factory = factory
|
869 | exports.lazy = false // no lazy loading, as we alter type.Matrix._storage
|