feat: matrix subset according to type of input (#3485)

This commit is contained in:
David Contreras 2025-07-16 02:40:13 -06:00 committed by GitHub
parent 6b3b722140
commit 10602fdbfd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 481 additions and 165 deletions

View File

@ -21,6 +21,16 @@
method `unit.toBest(...)` (#3484). Thanks @Mundi93, @EliaAlesiani, and
@HeavyRainLQ.
# unpublished changes since 14.5.2
!!! BE CAREFUL: BREAKING CHANGES !!!
- Fix: #1753 Correct dimensionality of Kronecker product on vectors (and
extend to arbitrary dimension) (#3455). Thanks @Delaney.
- Feat: #3349 Decouple precedence of unary percentage operator and binary
modulus operator (that both use symbol `%`), and raise the former (#3432).
Thanks @kiprobinsonknack.
# 2025-07-02, 14.5.3
- Fix: #2199 parse non-breaking white space ` ` as white space
@ -41,6 +51,8 @@
# 2025-05-28, 14.5.1
# Unpublished changes since 14.4.0
- Fix: #3482 mathjs throwing an error related to `BigInt` when loading in
specific environments.
- Fix: syntax section of function `numeric` (see #3448).

View File

@ -15,7 +15,8 @@ const config = {
numberFallback: 'number',
precision: 64,
predictable: false,
randomSeed: null
randomSeed: null,
legacySubset: false
}
const math = create(all, config)
@ -84,6 +85,8 @@ The following configuration options are available:
- `randomSeed`. Set this option to seed pseudo random number generation, making it deterministic. The pseudo random number generator is reset with the seed provided each time this option is set. For example, setting it to `'a'` will cause `math.random()` to return `0.43449421599986604` upon the first call after setting the option every time. Set to `null` to seed the pseudo random number generator with a random seed. Default value is `null`.
- `legacySubset`. When set to `true`, the `subset` function behaves as in earlier versions of math.js: retrieving a subset where the index size contains only one element, returns the value itself. When set to `false` (default), `subset` eliminates dimensions from the result only if the index dimension is a scalar; if the index dimension is a range, matrix, or array, the dimensions are preserved. This option is helpful for maintaining compatibility with legacy code that depends on the previous behavior.
## Examples

View File

@ -270,13 +270,34 @@ in the matrix, and if not, a subset of the matrix will be returned.
A subset can be defined using an `Index`. An `Index` contains a single value
or a set of values for each dimension of a matrix. An `Index` can be
created using the function `index`. When getting a single value from a matrix,
`subset` will return the value itself instead of a matrix containing just this
value.
created using the function `index`. The way `subset` returns results depends on how you specify indices for each dimension:
The function `subset` normally returns a subset, but when getting or setting a
single value in a matrix, the value itself is returned.
- If you use a scalar (single number) as an index for a dimension, that dimension is removed from the result.
- If you use an array, matrix or range (even with just one element) as an index, that dimension is preserved in the result.
This means that scalar indices eliminate dimensions, while array, matrix or range indices retain them. See the section [Migrate to v15](#migrate-to-v15) for more details and examples of this behavior.
For example:
```js
const m = [
[10, 11, 12],
[20, 21, 22]
]
// Scalar index eliminates the dimension:
math.subset(m, math.index(1, 2)) // 22 (both dimensions indexed by scalars, result is a value)
math.subset(m, math.index(1, [2])) // [22] (row dimension eliminated, column dimension preserved as array)
math.subset(m, math.index([1], 2)) // [22] (column dimension eliminated, row dimension preserved as array)
math.subset(m, math.index([1], [2])) // [[22]] (both dimensions preserved as arrays)
math.config({legacySubset: true}) // switch to legacy behavior
math.subset(m, math.index(1, 2)) // 22
math.subset(m, math.index(1, [2])) // 22
math.subset(m, math.index([1], 2)) // 22
math.subset(m, math.index([1], [2])) // 22
```
Matrix indexes in math.js are zero-based, like most programming languages
including JavaScript itself. Note that mathematical applications like Matlab
@ -296,7 +317,7 @@ math.subset(a, math.index([2, 3])) // Array, [2, 3]
math.subset(a, math.index(math.range(0,4))) // Array, [0, 1, 2, 3]
math.subset(b, math.index(1, 0)) // 2
math.subset(b, math.index(1, [0, 1])) // Array, [2, 3]
math.subset(b, math.index([0, 1], 0)) // Matrix, [[0], [2]]
math.subset(b, math.index([0, 1], [0])) // Matrix, [[0], [2]]
// get a subset
d.subset(math.index([1, 2], [0, 1])) // Matrix, [[3, 4], [6, 7]]
@ -312,6 +333,39 @@ c.subset(math.index(1, [0, 1]), [2, 3]) // Matrix, [[0, 1], [2, 3]]
e.resize([2, 3], 0) // Matrix, [[0, 0, 0], [0, 0, 0]]
e.subset(math.index(1, 2), 5) // Matrix, [[0, 0, 0], [0, 0, 5]]
```
## Migrate to v15
With the release of math.js v15, the behavior of `subset` when indexing matrices and arrays has changed. If your code relies on the previous behavior (where indexing with an array or matrix of size 1 would always return the value itself), you may need to update your code or enable legacy mode.
### How to migrate
- **Option 1: Enable legacy behavior**
If you want your code to work as before without changes, enable legacy mode by adding:
```js
math.config({ legacySubset: true })
```
This restores the old behavior for `subset`.
- **Option 2: Update your code**
Update your code to use scalar indices when you want to eliminate dimensions, or use array indices to preserve dimensions.
### Migration examples
```js
const m = math.matrix([[1, 2, 3], [4, 5, 6]])
```
| v14 code | v15 equivalent code | Result |
|----------------------------------------------|-------------------------------------------|--------------------|
| `math.subset(m, math.index([0, 1], [1, 2]))` | No change needed | `[[2, 3], [5, 6]]` |
| `math.subset(m, math.index(1, [1, 2]))` | `math.subset(m, math.index([1], [1, 2]))` | `[[5, 6]]` |
| `math.subset(m, math.index([0, 1], 2))` | `math.subset(m, math.index([0, 1], [2]))` | `[[3], [6]]` |
| `math.subset(m, math.index(1, 2))` | No change needed | 6 |
> **Tip:**
> If you want to get a scalar value, use scalar indices.
> If you want to preserve dimensions, use array, matrix or range indices.
## Getting and setting a value in a matrix

View File

@ -680,6 +680,10 @@ resolving of the nested index needs to be split in two separate operations.
in JavaScript: They are one-based with an included upper-bound, similar to most
math applications.*
*IMPORTANT: Matrix indexing behaves differently depending on the type of input.
If an index for a dimension is a scalar (single value), that dimension is
removed from the result. For more information of the changes go to [Migrate to V15](../datatypes/matrices.md#migrate-to-v15).*
```js
parser = math.parser()
@ -697,10 +701,33 @@ parser.evaluate('d = a * b') // Matrix, [[19, 22], [43, 50]]
// retrieve a subset of a matrix
parser.evaluate('d[2, 1]') // 43
parser.evaluate('d[2, 1:end]') // Matrix, [[43, 50]]
parser.evaluate('d[2, 1:end]') // Matrix, [43, 50]
parser.evaluate('c[end - 1 : -1 : 2]') // Matrix, [8, 7, 6]
```
#### Matrix Migration examples
With v15, matrix indexing has changed to be more consistent and predictable. In v14, using a scalar index would sometimes reduce the dimensionality of the result. In v15, if you want to preserve dimensions, use array, matrix, or range indices. If you want a scalar value, use scalar indices.
For example:
```js
parser = math.parser()
parser.evaluate('m = [1, 2, 3; 4, 5, 6]')
```
| v14 code | v15 equivalent code | Result |
|-------------------------|-------------------------------|--------------------|
| `m[1:2, 2:3]` | No change needed | `[[2, 3], [5, 6]]` |
| `m[2, 2:3]` | `m[[2], 2:3]` | `[[5, 6]]` |
| `m[1:2, 3]` | `m[1:2, [3]]` | `[[3], [6]]` |
| `m[2, 3]` | No change needed | `6` |
> **Tip:**
> If you want to always get a scalar value, use scalar indices.
> If you want to preserve dimensions, use array, matrix or range indices.
## Objects
Objects in math.js work the same as in languages like JavaScript and Python.

View File

@ -28,5 +28,10 @@ export const DEFAULT_CONFIG = {
// random seed for seeded pseudo random number generation
// null = randomly seed
randomSeed: null
randomSeed: null,
// legacy behavior for matrix subset. When true, the subset function
// returns a matrix or array with the same size as the index (except for scalars).
// When false, it returns a matrix or array with a size depending on the type of index.
legacySubset: false
}

View File

@ -61,6 +61,11 @@ export function configFactory (config, emit) {
delete optionsFix.epsilon
return _config(optionsFix)
}
if (options.legacySubset === true) {
// this if is only for backwards compatibility, it can be removed in the future.
console.warn('Warning: The configuration option "legacySubset" is for compatibility only and might be deprecated in the future.')
}
const prev = clone(config)
// validate some of the options

View File

@ -35,7 +35,8 @@ export const createSylvester = /* #__PURE__ */ factory(name, dependencies, (
subtract,
identity,
lusolve,
abs
abs,
config
}
) => {
/**
@ -110,7 +111,7 @@ export const createSylvester = /* #__PURE__ */ factory(name, dependencies, (
for (let k = 0; k < n; k++) {
if (k < (n - 1) && abs(subset(G, index(k + 1, k))) > 1e-5) {
let RHS = vc(subset(D, index(all, k)), subset(D, index(all, k + 1)))
let RHS = vc(subset(D, index(all, [k])), subset(D, index(all, [k + 1])))
for (let j = 0; j < k; j++) {
RHS = add(RHS,
vc(multiply(y[j], subset(G, index(j, k))), multiply(y[j], subset(G, index(j, k + 1))))
@ -125,11 +126,11 @@ export const createSylvester = /* #__PURE__ */ factory(name, dependencies, (
hc(gkm, add(F, gmm))
)
const yAux = lusolve(LHS, RHS)
y[k] = yAux.subset(index(range(0, m), 0))
y[k + 1] = yAux.subset(index(range(m, 2 * m), 0))
y[k] = yAux.subset(index(range(0, m), [0]))
y[k + 1] = yAux.subset(index(range(m, 2 * m), [0]))
k++
} else {
let RHS = subset(D, index(all, k))
let RHS = subset(D, index(all, [k]))
for (let j = 0; j < k; j++) { RHS = add(RHS, multiply(y[j], subset(G, index(j, k)))) }
const gkk = subset(G, index(k, k))
const LHS = subtract(F, multiply(gkk, identity(m)))
@ -139,7 +140,6 @@ export const createSylvester = /* #__PURE__ */ factory(name, dependencies, (
}
const Y = matrix(matrixFromColumns(...y))
const X = multiply(U, multiply(Y, transpose(V)))
return X
}
})

View File

@ -51,8 +51,9 @@ export const createColumn = /* #__PURE__ */ factory(name, dependencies, ({ typed
validateIndex(column, value.size()[1])
const rowRange = range(0, value.size()[0])
const index = new Index(rowRange, column)
const index = new Index(rowRange, [column])
const result = value.subset(index)
// once config.legacySubset just return result
return isMatrix(result)
? result
: matrix([[result]])

View File

@ -51,8 +51,9 @@ export const createRow = /* #__PURE__ */ factory(name, dependencies, ({ typed, I
validateIndex(row, value.size()[0])
const columnRange = range(0, value.size()[1])
const index = new Index(row, columnRange)
const index = new Index([row], columnRange)
const result = value.subset(index)
// once config.legacySubset just return result
return isMatrix(result)
? result
: matrix([[result]])

View File

@ -21,8 +21,8 @@ export const createSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed
* // get a subset
* const d = [[1, 2], [3, 4]]
* math.subset(d, math.index(1, 0)) // returns 3
* math.subset(d, math.index([0, 1], 1)) // returns [[2], [4]]
* math.subset(d, math.index([false, true], 0)) // returns [[3]]
* math.subset(d, math.index([0, 1], [1])) // returns [[2], [4]]
* math.subset(d, math.index([false, true], [0])) // returns [[3]]
*
* // replace a subset
* const e = []
@ -32,9 +32,9 @@ export const createSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed
*
* // get submatrix using ranges
* const M = [
* [1,2,3],
* [4,5,6],
* [7,8,9]
* [1, 2, 3],
* [4, 5, 6],
* [7, 8, 9]
* ]
* math.subset(M, math.index(math.range(0,2), math.range(0,3))) // [[1, 2, 3], [4, 5, 6]]
*
@ -115,7 +115,7 @@ export const createSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed
if (typeof replacement === 'string') {
throw new Error('can\'t boradcast a string')
}
if (index._isScalar) {
if (index.isScalar()) {
return replacement
}
@ -160,9 +160,14 @@ function _getSubstring (str, index) {
const range = index.dimension(0)
let substr = ''
range.forEach(function (v) {
function callback (v) {
substr += str.charAt(v)
})
}
if (Number.isInteger(range)) {
callback(range)
} else {
range.forEach(callback)
}
return substr
}
@ -196,7 +201,7 @@ function _setSubstring (str, index, replacement, defaultValue) {
}
const range = index.dimension(0)
const len = range.size()[0]
const len = Number.isInteger(range) ? 1 : range.size()[0]
if (len !== replacement.length) {
throw new DimensionError(range.size()[0], replacement.length)
@ -213,9 +218,15 @@ function _setSubstring (str, index, replacement, defaultValue) {
chars[i] = str.charAt(i)
}
range.forEach(function (v, i) {
function callback (v, i) {
chars[v] = replacement.charAt(i[0])
})
}
if (Number.isInteger(range)) {
callback(range, [0])
} else {
range.forEach(callback)
}
// initialize undefined characters with a space
if (chars.length > strLen) {

View File

@ -10,10 +10,11 @@ import { optimizeCallback } from '../../utils/optimizeCallback.js'
const name = 'DenseMatrix'
const dependencies = [
'Matrix'
'Matrix',
'config'
]
export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies, ({ Matrix }) => {
export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies, ({ Matrix, config }) => {
/**
* Dense Matrix implementation. A regular, dense matrix, supporting multi-dimensional matrices. This is the default matrix type.
* @class DenseMatrix
@ -218,7 +219,9 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
throw new TypeError('Invalid index')
}
const isScalar = index.isScalar()
const isScalar = config.legacySubset
? index.size().every(idx => idx === 1)
: index.isScalar()
if (isScalar) {
// return a scalar
return matrix.get(index.min())
@ -238,12 +241,12 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
}
// retrieve submatrix
const returnMatrix = new DenseMatrix([])
const returnMatrix = new DenseMatrix()
const submatrix = _getSubmatrix(matrix._data, index)
returnMatrix._size = submatrix.size
returnMatrix._datatype = matrix._datatype
returnMatrix._data = submatrix.data
return returnMatrix
return config.legacySubset ? returnMatrix.reshape(index.size()) : returnMatrix
}
}
@ -259,21 +262,31 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
function _getSubmatrix (data, index) {
const maxDepth = index.size().length - 1
const size = Array(maxDepth)
return { data: getSubmatrixRecursive(data), size }
return { data: getSubmatrixRecursive(data), size: size.filter(x => x !== null) }
function getSubmatrixRecursive (data, depth = 0) {
const ranges = index.dimension(depth)
size[depth] = ranges.size()[0]
if (depth < maxDepth) {
return ranges.map(rangeIndex => {
validateIndex(rangeIndex, data.length)
return getSubmatrixRecursive(data[rangeIndex], depth + 1)
}).valueOf()
const dims = index.dimension(depth)
function _mapIndex (dim, callback) {
// applies a callback for when the index is a Number or a Matrix
if (isNumber(dim)) return callback(dim)
else return dim.map(callback).valueOf()
}
if (isNumber(dims)) {
size[depth] = null
} else {
return ranges.map(rangeIndex => {
validateIndex(rangeIndex, data.length)
return data[rangeIndex]
}).valueOf()
size[depth] = dims.size()[0]
}
if (depth < maxDepth) {
return _mapIndex(dims, dimIndex => {
validateIndex(dimIndex, data.length)
return getSubmatrixRecursive(data[dimIndex], depth + 1)
})
} else {
return _mapIndex(dims, dimIndex => {
validateIndex(dimIndex, data.length)
return data[dimIndex]
})
}
}
}
@ -300,19 +313,19 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
const isScalar = index.isScalar()
// calculate the size of the submatrix, and convert it into an Array if needed
let sSize
let submatrixSize
if (isMatrix(submatrix)) {
sSize = submatrix.size()
submatrixSize = submatrix.size()
submatrix = submatrix.valueOf()
} else {
sSize = arraySize(submatrix)
submatrixSize = arraySize(submatrix)
}
if (isScalar) {
// set a scalar
// check whether submatrix is a scalar
if (sSize.length !== 0) {
if (submatrixSize.length !== 0) {
throw new TypeError('Scalar expected')
}
matrix.set(index.min(), submatrix, defaultValue)
@ -320,16 +333,16 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
// set a submatrix
// broadcast submatrix
if (!deepStrictEqual(sSize, iSize)) {
try {
if (sSize.length === 0) {
submatrix = broadcastTo([submatrix], iSize)
} else {
if (!deepStrictEqual(submatrixSize, iSize)) {
if (submatrixSize.length === 0) {
submatrix = broadcastTo([submatrix], iSize)
} else {
try {
submatrix = broadcastTo(submatrix, iSize)
} catch (error) {
}
sSize = arraySize(submatrix)
} catch {
}
submatrixSize = arraySize(submatrix)
}
// validate dimensions
@ -337,11 +350,11 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
throw new DimensionError(iSize.length, matrix._size.length, '<')
}
if (sSize.length < iSize.length) {
if (submatrixSize.length < iSize.length) {
// calculate number of missing outer dimensions
let i = 0
let outer = 0
while (iSize[i] === 1 && sSize[i] === 1) {
while (iSize[i] === 1 && submatrixSize[i] === 1) {
i++
}
while (iSize[i] === 1) {
@ -350,12 +363,12 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
}
// unsqueeze both outer and inner dimensions
submatrix = unsqueeze(submatrix, iSize.length, outer, sSize)
submatrix = unsqueeze(submatrix, iSize.length, outer, submatrixSize)
}
// check whether the size of the submatrix matches the index size
if (!deepStrictEqual(iSize, sSize)) {
throw new DimensionError(iSize, sSize, '>')
if (!deepStrictEqual(iSize, submatrixSize)) {
throw new DimensionError(iSize, submatrixSize, '>')
}
// enlarge matrix when needed
@ -386,16 +399,21 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
function setSubmatrixRecursive (data, submatrix, depth = 0) {
const range = index.dimension(depth)
const recursiveCallback = (rangeIndex, i) => {
validateIndex(rangeIndex, data.length)
setSubmatrixRecursive(data[rangeIndex], submatrix[i[0]], depth + 1)
}
const finalCallback = (rangeIndex, i) => {
validateIndex(rangeIndex, data.length)
data[rangeIndex] = submatrix[i[0]]
}
if (depth < maxDepth) {
range.forEach((rangeIndex, i) => {
validateIndex(rangeIndex, data.length)
setSubmatrixRecursive(data[rangeIndex], submatrix[i[0]], depth + 1)
})
if (isNumber(range)) recursiveCallback(range, [0])
else range.forEach(recursiveCallback)
} else {
range.forEach((rangeIndex, i) => {
validateIndex(rangeIndex, data.length)
data[rangeIndex] = submatrix[i[0]]
})
if (isNumber(range)) finalCallback(range, [0])
else range.forEach(finalCallback)
}
}
}

View File

@ -1,4 +1,4 @@
import { isArray, isMatrix, isRange } from '../../utils/is.js'
import { isArray, isMatrix, isRange, isNumber, isString } from '../../utils/is.js'
import { clone } from '../../utils/object.js'
import { isInteger } from '../../utils/number.js'
import { factory } from '../../utils/factory.js'
@ -29,7 +29,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
* @Constructor Index
* @param {...*} ranges
*/
function Index (ranges) {
function Index (...ranges) {
if (!(this instanceof Index)) {
throw new SyntaxError('Constructor must be called with the new operator')
}
@ -38,8 +38,8 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
this._sourceSize = []
this._isScalar = true
for (let i = 0, ii = arguments.length; i < ii; i++) {
const arg = arguments[i]
for (let i = 0, ii = ranges.length; i < ii; i++) {
const arg = ranges[i]
const argIsArray = isArray(arg)
const argIsMatrix = isMatrix(arg)
const argType = typeof arg
@ -50,6 +50,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
} else if (argIsArray || argIsMatrix) {
// create matrix
let m
this._isScalar = false
if (getMatrixDataType(arg) === 'boolean') {
if (argIsArray) m = _createImmutableMatrix(_booleansArrayToNumbersForIndex(arg).valueOf())
@ -60,16 +61,10 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
}
this._dimensions.push(m)
// size
const size = m.size()
// scalar
if (size.length !== 1 || size[0] !== 1 || sourceSize !== null) {
this._isScalar = false
}
} else if (argType === 'number') {
this._dimensions.push(_createImmutableMatrix([arg]))
this._dimensions.push(arg)
} else if (argType === 'bigint') {
this._dimensions.push(_createImmutableMatrix([Number(arg)]))
this._dimensions.push(Number(arg))
} else if (argType === 'string') {
// object property (arguments.count should be 1)
this._dimensions.push(arg)
@ -90,12 +85,15 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
function _createImmutableMatrix (arg) {
// loop array elements
for (let i = 0, l = arg.length; i < l; i++) {
if (typeof arg[i] !== 'number' || !isInteger(arg[i])) {
if (!isNumber(arg[i]) || !isInteger(arg[i])) {
throw new TypeError('Index parameters must be positive integer numbers')
}
}
// create matrix
return new ImmutableDenseMatrix(arg)
const matrix = new ImmutableDenseMatrix()
matrix._data = arg
matrix._size = [arg.length]
return matrix
}
/**
@ -134,7 +132,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
for (let i = 0, ii = this._dimensions.length; i < ii; i++) {
const d = this._dimensions[i]
size[i] = (typeof d === 'string') ? 1 : d.size()[0]
size[i] = (isString(d) || isNumber(d)) ? 1 : d.size()[0]
}
return size
@ -150,7 +148,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
for (let i = 0, ii = this._dimensions.length; i < ii; i++) {
const range = this._dimensions[i]
values[i] = (typeof range === 'string') ? range : range.max()
values[i] = (isString(range) || isNumber(range)) ? range : range.max()
}
return values
@ -166,7 +164,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
for (let i = 0, ii = this._dimensions.length; i < ii; i++) {
const range = this._dimensions[i]
values[i] = (typeof range === 'string') ? range : range.min()
values[i] = (isString(range) || isNumber(range)) ? range : range.min()
}
return values
@ -192,11 +190,11 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
* @returns {Range | null} range
*/
Index.prototype.dimension = function (dim) {
if (typeof dim !== 'number') {
if (!isNumber(dim)) {
return null
}
return this._dimensions[dim] || null
return this._dimensions[dim] ?? null
}
/**
@ -204,7 +202,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
* @returns {boolean} Returns true if the index is an object property
*/
Index.prototype.isObjectProperty = function () {
return this._dimensions.length === 1 && typeof this._dimensions[0] === 'string'
return this._dimensions.length === 1 && isString(this._dimensions[0])
}
/**
@ -238,7 +236,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
const array = []
for (let i = 0, ii = this._dimensions.length; i < ii; i++) {
const dimension = this._dimensions[i]
array.push((typeof dimension === 'string') ? dimension : dimension.toArray())
array.push(isString(dimension) || isNumber(dimension) ? dimension : dimension.toArray())
}
return array
}
@ -261,7 +259,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
for (let i = 0, ii = this._dimensions.length; i < ii; i++) {
const dimension = this._dimensions[i]
if (typeof dimension === 'string') {
if (isString(dimension)) {
strings.push(JSON.stringify(dimension))
} else {
strings.push(dimension.toString())

View File

@ -295,12 +295,14 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie
const pv = []
// loop rows in resulting matrix
rows.forEach(function (i, r) {
function rowsCallback (i, r) {
// update permutation vector
pv[i] = r[0]
// mark i in workspace
w[i] = true
})
}
if (Number.isInteger(rows)) rowsCallback(rows, [0])
else rows.forEach(rowsCallback)
// result matrix arrays
const values = mvalues ? [] : undefined
@ -308,7 +310,7 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie
const ptr = []
// loop columns in result matrix
columns.forEach(function (j) {
function columnsCallback (j) {
// update ptr
ptr.push(index.length)
// loop values in column j
@ -323,7 +325,9 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie
if (values) { values.push(mvalues[k]) }
}
}
})
}
if (Number.isInteger(columns)) columnsCallback(columns)
else columns.forEach(columnsCallback)
// update ptr
ptr.push(index.length)
@ -398,7 +402,7 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie
if (iSize.length === 1) {
// if the replacement index only has 1 dimension, go trough each one and set its value
const range = index.dimension(0)
range.forEach(function (dataIndex, subIndex) {
_forEachIndex(range, (dataIndex, subIndex) => {
validateIndex(dataIndex)
matrix.set([dataIndex, 0], submatrix[subIndex[0]], defaultValue)
})
@ -406,9 +410,9 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie
// if the replacement index has 2 dimensions, go through each one and set the value in the correct index
const firstDimensionRange = index.dimension(0)
const secondDimensionRange = index.dimension(1)
firstDimensionRange.forEach(function (firstDataIndex, firstSubIndex) {
_forEachIndex(firstDimensionRange, (firstDataIndex, firstSubIndex) => {
validateIndex(firstDataIndex)
secondDimensionRange.forEach(function (secondDataIndex, secondSubIndex) {
_forEachIndex(secondDimensionRange, (secondDataIndex, secondSubIndex) => {
validateIndex(secondDataIndex)
matrix.set([firstDataIndex, secondDataIndex], submatrix[firstSubIndex[0]][secondSubIndex[0]], defaultValue)
})
@ -416,6 +420,12 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie
}
}
return matrix
function _forEachIndex (index, callback) {
// iterate cases where index is a Matrix or a Number
if (isNumber(index)) callback(index, [0])
else index.forEach(callback)
}
}
/**

View File

@ -37,4 +37,28 @@ describe('config', function () {
// Restore console.warn
warnStub.restore()
})
it('should work with config legacySubset during deprecation', function () {
const math2 = math.create()
// Add a spy to temporarily disable console.warn
const warnStub = sinon.stub(console, 'warn')
// Set legacySubset to true and should throw a warning
assert.doesNotThrow(function () { math2.config({ legacySubset: true }) })
// Check if legacySubset is set
assert.strictEqual(math2.config().legacySubset, true)
// Check if console.warn was called
assert.strictEqual(warnStub.callCount, 1)
// Set legacySubset to false, should not throw a warning
assert.doesNotThrow(function () { math2.config({ legacySubset: false }) })
// Validate that if console.warn was not called again
assert.strictEqual(warnStub.callCount, 1)
// Restore console.warn
warnStub.restore()
})
})

View File

@ -73,7 +73,7 @@ describe('AccessorNode', function () {
const scope = {
a: [[1, 2], [3, 4]]
}
assert.deepStrictEqual(expr.evaluate(scope), [[3, 4]])
assert.deepStrictEqual(expr.evaluate(scope), [3, 4])
})
it('should compile a AccessorNode with "end" in an expression', function () {
@ -185,7 +185,7 @@ describe('AccessorNode', function () {
const scope = {
a: [[1, 2], [3, 4]]
}
assert.deepStrictEqual(expr.evaluate(scope), [[4, 3]])
assert.deepStrictEqual(expr.evaluate(scope), [4, 3])
})
it('should compile a AccessorNode with "end" both as value and in a range', function () {
@ -203,7 +203,7 @@ describe('AccessorNode', function () {
const scope = {
a: [[1, 2], [3, 4]]
}
assert.deepStrictEqual(expr.evaluate(scope), [[3, 4]])
assert.deepStrictEqual(expr.evaluate(scope), [3, 4])
})
it('should use the inner context when using "end" in a nested index', function () {

View File

@ -715,18 +715,18 @@ describe('parse', function () {
[7, 8, 9]
])
}
assert.deepStrictEqual(parseAndEval('a[2, :]', scope), math.matrix([[4, 5, 6]]))
assert.deepStrictEqual(parseAndEval('a[2, :2]', scope), math.matrix([[4, 5]]))
assert.deepStrictEqual(parseAndEval('a[2, :end-1]', scope), math.matrix([[4, 5]]))
assert.deepStrictEqual(parseAndEval('a[2, 2:]', scope), math.matrix([[5, 6]]))
assert.deepStrictEqual(parseAndEval('a[2, 2:3]', scope), math.matrix([[5, 6]]))
assert.deepStrictEqual(parseAndEval('a[2, 1:2:3]', scope), math.matrix([[4, 6]]))
assert.deepStrictEqual(parseAndEval('a[:, 2]', scope), math.matrix([[2], [5], [8]]))
assert.deepStrictEqual(parseAndEval('a[:2, 2]', scope), math.matrix([[2], [5]]))
assert.deepStrictEqual(parseAndEval('a[:end-1, 2]', scope), math.matrix([[2], [5]]))
assert.deepStrictEqual(parseAndEval('a[2:, 2]', scope), math.matrix([[5], [8]]))
assert.deepStrictEqual(parseAndEval('a[2:3, 2]', scope), math.matrix([[5], [8]]))
assert.deepStrictEqual(parseAndEval('a[1:2:3, 2]', scope), math.matrix([[2], [8]]))
assert.deepStrictEqual(parseAndEval('a[2, :]', scope), math.matrix([4, 5, 6]))
assert.deepStrictEqual(parseAndEval('a[2, :2]', scope), math.matrix([4, 5]))
assert.deepStrictEqual(parseAndEval('a[2, :end-1]', scope), math.matrix([4, 5]))
assert.deepStrictEqual(parseAndEval('a[2, 2:]', scope), math.matrix([5, 6]))
assert.deepStrictEqual(parseAndEval('a[2, 2:3]', scope), math.matrix([5, 6]))
assert.deepStrictEqual(parseAndEval('a[2, 1:2:3]', scope), math.matrix([4, 6]))
assert.deepStrictEqual(parseAndEval('a[:, 2]', scope), math.matrix([2, 5, 8]))
assert.deepStrictEqual(parseAndEval('a[:2, [2]]', scope), math.matrix([[2], [5]]))
assert.deepStrictEqual(parseAndEval('a[:end-1, [2]]', scope), math.matrix([[2], [5]]))
assert.deepStrictEqual(parseAndEval('a[2:, [2]]', scope), math.matrix([[5], [8]]))
assert.deepStrictEqual(parseAndEval('a[2:3, [2]]', scope), math.matrix([[5], [8]]))
assert.deepStrictEqual(parseAndEval('a[1:2:3, [2]]', scope), math.matrix([[2], [8]]))
})
it('should get a matrix subset of a matrix subset', function () {
@ -737,7 +737,7 @@ describe('parse', function () {
[7, 8, 9]
])
}
assert.deepStrictEqual(parseAndEval('a[2, :][1,1]', scope), 4)
assert.deepStrictEqual(parseAndEval('a[[2], :][1,1]', scope), 4)
})
it('should get BigNumber value from an array', function () {
@ -786,7 +786,7 @@ describe('parse', function () {
assert.deepStrictEqual(parseAndEval('a[1:3,1:2]', scope), math.matrix([[100, 2], [3, 10], [0, 12]]))
scope.b = [[1, 2], [3, 4]]
assert.deepStrictEqual(parseAndEval('b[1,:]', scope), [[1, 2]])
assert.deepStrictEqual(parseAndEval('b[1,:]', scope), [1, 2])
})
it('should get/set the matrix correctly for 3d matrices', function () {
@ -807,10 +807,10 @@ describe('parse', function () {
]))
assert.deepStrictEqual(parseAndEval('size(f)', scope), math.matrix([2, 2, 2], 'dense', 'number'))
assert.deepStrictEqual(parseAndEval('f[:,:,1]', scope), math.matrix([[[1], [2]], [[3], [4]]]))
assert.deepStrictEqual(parseAndEval('f[:,:,2]', scope), math.matrix([[[5], [6]], [[7], [8]]]))
assert.deepStrictEqual(parseAndEval('f[:,2,:]', scope), math.matrix([[[2, 6]], [[4, 8]]]))
assert.deepStrictEqual(parseAndEval('f[2,:,:]', scope), math.matrix([[[3, 7], [4, 8]]]))
assert.deepStrictEqual(parseAndEval('f[:,:,1]', scope), math.matrix([[1, 2], [3, 4]]))
assert.deepStrictEqual(parseAndEval('f[:,:,2]', scope), math.matrix([[5, 6], [7, 8]]))
assert.deepStrictEqual(parseAndEval('f[:,2,:]', scope), math.matrix([[2, 6], [4, 8]]))
assert.deepStrictEqual(parseAndEval('f[2,:,:]', scope), math.matrix([[3, 7], [4, 8]]))
parseAndEval('a=diag([1,2,3,4])', scope)
assert.deepStrictEqual(parseAndEval('a[3:end, 3:end]', scope), math.matrix([[3, 0], [0, 4]]))

View File

@ -1,7 +1,7 @@
// test schur decomposition
import assert from 'assert'
import math from '../../../../src/defaultInstance.js'
import sinon from 'sinon'
describe('sylvester', function () {
it('should solve sylvester equation of order 5 with Arrays', function () {
@ -53,4 +53,51 @@ describe('sylvester', function () {
[-1.0935231387905004, 4.113817086842746, 5.747671819196675, -0.9408309030864932, 2.967655969930743]
])), 'fro') < 1e-3)
})
it('should work with config legacySubset during deprecation', function () {
const math2 = math.create()
// Add a spy to temporarily disable console.warn
const warnStub = sinon.stub(console, 'warn')
math2.config({ legacySubset: true })
// Test legacy syntax with sylvester
// This is not strictly necessary and shoudl be removed after the deprecation period
const sylvesterA = [[-5.3, -1.4, -0.2, 0.7],
[-0.4, -1.0, -0.1, -1.2],
[0.3, 0.7, -2.5, 0.7],
[3.6, -0.1, 1.4, -2.4]]
const sylvesterB = [
[1.1, -0.3, -0.9, 0.8, -2.5],
[-0.6, 2.6, 0.2, 0.4, 1.3],
[0.4, -0.5, -0.2, 0.2, -0.1],
[-0.4, -1.9, -0.2, 0.5, 1.4],
[0.4, -1.0, -0.1, -0.8, -1.3]
]
const sylvesterC = [
[1.4, 1.1, -1.9, 0.1, 1.2],
[-1.7, 0.1, -0.4, 2.1, 0.5],
[1.9, 2.3, -0.8, -0.7, 1.0],
[-1.1, 2.8, -0.8, -0.3, 0.9]
]
const sylvesterSol = [
[-0.19393862606643053, -0.17101629636521865, 2.709348263225366, -0.0963000767188319, 0.5244718194343121],
[0.38421326955977486, -0.21159588555260944, -6.544262021555474, -0.15113424769761136, -2.312533293658291],
[-2.2708235174374747, 4.498279916441834, 1.4553799673144823, -0.9300926971755248, 2.5508111398452353],
[-1.0935231387905004, 4.113817086842746, 5.747671819196675, -0.9408309030864932, 2.967655969930743]
]
assert.ok(math2.norm(math2.subtract(
math2.sylvester(sylvesterA, sylvesterB, sylvesterC), sylvesterSol), 'fro') < 1e-3)
// Test without legacySubset
math2.config({ legacySubset: false })
assert.ok(math2.norm(math2.subtract(
math2.sylvester(sylvesterA, sylvesterB, sylvesterC), sylvesterSol), 'fro') < 1e-3)
// Restore console.warn
warnStub.restore()
})
})

View File

@ -1,5 +1,6 @@
import assert from 'assert'
import math from '../../../../src/defaultInstance.js'
import sinon from 'sinon'
const column = math.column
const matrix = math.matrix
@ -107,4 +108,37 @@ describe('column', function () {
c.valueOf(), [[0], [0], [0], [0], [0]]
)
})
it('should work with config legacySubset during deprecation', function () {
const math2 = math.create()
// Add a spy to temporarily disable console.warn
const warnStub = sinon.stub(console, 'warn')
math2.config({ legacySubset: true })
const a = [
[0, 2, 0, 0, 0],
[0, 1, 0, 2, 4],
[0, 0, 0, 0, 0],
[8, 4, 0, 3, 0],
[0, 0, 0, 6, 0]
]
// Test column with legacySubset syntax
// This is not strictly necessary and shoudl be removed after the deprecation period
assert.deepStrictEqual(
math2.column(a, 4).valueOf(), [[0], [4], [0], [0], [0]]
)
// Test column with legacySubset syntax
math2.config({ legacySubset: false })
// Test column without legacySubset syntax
assert.deepStrictEqual(
math2.column(a, 4).valueOf(), [[0], [4], [0], [0], [0]]
)
// Restore console.warn
warnStub.restore()
})
})

View File

@ -281,7 +281,7 @@ describe('map', function () {
it('should operate from the parser with multiple inputs that need broadcasting and one based indices and the broadcasted arrays', function () {
// this is a convoluted way of calculating f(a,b,idx) = 2a+2b+index
// 2(1) + 2([3,4]) + [1, 2] # yields [9, 12]
const arr2 = math.evaluate('map([1],[3,4], f(a,b,idx,A,B)= a + A[idx] + b + B[idx] + idx[1])')
const arr2 = math.evaluate('map([1],[3,4], f(a,b,idx,A,B)= a + A[idx[1]] + b + B[idx[1]] + idx[1])')
const expected = math.matrix([9, 12])
assert.deepStrictEqual(arr2, expected)
})

View File

@ -1,5 +1,6 @@
import assert from 'assert'
import math from '../../../../src/defaultInstance.js'
import sinon from 'sinon'
const row = math.row
const matrix = math.matrix
@ -107,4 +108,36 @@ describe('row', function () {
r.valueOf(), [[0, 0, 0, 0, 0]]
)
})
it('should work with config legacySubset during deprecation', function () {
const math2 = math.create()
// Add a spy to temporarily disable console.warn
const warnStub = sinon.stub(console, 'warn')
math2.config({ legacySubset: true })
const a = [
[0, 2, 0, 0, 0],
[0, 1, 0, 2, 4],
[0, 0, 0, 0, 0],
[8, 4, 0, 3, 0],
[0, 0, 0, 6, 0]
]
// Test row with legacySubset syntax
// This is not strictly necessary and should be removed after the deprecation period
assert.deepStrictEqual(
math2.row(a, 3).valueOf(), [[8, 4, 0, 3, 0]]
)
// Test row without legacySubset syntax
math2.config({ legacySubset: false })
assert.deepStrictEqual(
math2.row(a, 3).valueOf(), [[8, 4, 0, 3, 0]]
)
// Restore console.warn
warnStub.restore()
})
})

View File

@ -1,6 +1,8 @@
import assert from 'assert'
import math from '../../../../src/defaultInstance.js'
import { DimensionError } from '../../../../src/error/DimensionError.js'
import sinon from 'sinon'
const subset = math.subset
const matrix = math.matrix
const Range = math.Range
@ -11,13 +13,13 @@ describe('subset', function () {
const b = math.matrix(a)
it('should get the right subset of an array', function () {
assert.deepStrictEqual(subset(a, index(new Range(0, 2), 1)), [[2], [4]])
assert.deepStrictEqual(subset(a, index(new Range(0, 2), 1)), [2, 4])
assert.deepStrictEqual(subset(a, index(1, 0)), 3)
assert.deepStrictEqual(subset([math.bignumber(2)], index(0)), math.bignumber(2))
})
it('should get the right subset of an array of booleans', function () {
assert.deepStrictEqual(subset(a, index([true, true], 1)), [[2], [4]])
assert.deepStrictEqual(subset(a, index([true, true], [1])), [[2], [4]])
assert.deepStrictEqual(subset(a, index([false, true], [true, false])), [[3]])
assert.deepStrictEqual(subset([math.bignumber(2)], index([true])), [math.bignumber(2)])
})
@ -32,7 +34,7 @@ describe('subset', function () {
})
it('should get the right subset of an array of booleans in the parser', function () {
assert.deepStrictEqual(math.evaluate('a[[true, true], 2]', { a }), [[2], [4]])
assert.deepStrictEqual(math.evaluate('a[[true, true], 2]', { a }), [2, 4])
assert.deepStrictEqual(math.evaluate('a[[false, true], [true, false]]', { a }), [[3]])
assert.deepStrictEqual(math.evaluate('[bignumber(2)][[true]]'), math.matrix([math.bignumber(2)]))
})
@ -75,7 +77,7 @@ describe('subset', function () {
})
it('should get the right subset of a matrix', function () {
assert.deepStrictEqual(subset(b, index(new Range(0, 2), 1)), matrix([[2], [4]]))
assert.deepStrictEqual(subset(b, index(new Range(0, 2), 1)), matrix([2, 4]))
assert.deepStrictEqual(subset(b, index(1, 0)), 3)
})
@ -265,4 +267,36 @@ describe('subset', function () {
const expression = math.parse('subset([1],index(0,0))')
assert.strictEqual(expression.toTex(), '\\mathrm{subset}\\left(\\begin{bmatrix}1\\end{bmatrix},\\mathrm{index}\\left(0,0\\right)\\right)')
})
it('should work with config legacySubset during deprecation', function () {
const math2 = math.create()
// Add a spy to temporarily disable console.warn
const warnStub = sinon.stub(console, 'warn')
math2.config({ legacySubset: true })
// Test legacy syntax for getting a subset of a matrix
const A = math2.matrix([[1, 2, 3], [4, 5, 6]])
const index = math2.index
assert.deepStrictEqual(math2.subset(A, index(1, 2)), 6)
assert.deepStrictEqual(math2.subset(A, index([1], 2)), 6)
assert.deepStrictEqual(math2.subset(A, index(1, [2])), 6)
assert.deepStrictEqual(math2.subset(A, index([1], [2])), 6)
assert.deepStrictEqual(math2.subset(A, index(1, [1, 2])).toArray(), [[5, 6]])
assert.deepStrictEqual(math2.subset(A, index([0, 1], 1)).toArray(), [[2], [5]])
math2.config({ legacySubset: false })
// Test without legacy syntax
assert.deepStrictEqual(math2.subset(A, index(1, 2)), 6)
assert.deepStrictEqual(math2.subset(A, index([1], 2)).toArray(), [6])
assert.deepStrictEqual(math2.subset(A, index(1, [2])).toArray(), [6])
assert.deepStrictEqual(math2.subset(A, index([1], [2])).toArray(), [[6]])
assert.deepStrictEqual(math2.subset(A, index(1, [1, 2])).toArray(), [5, 6])
assert.deepStrictEqual(math2.subset(A, index([1], [1, 2])).toArray(), [[5, 6]])
assert.deepStrictEqual(math2.subset(A, index([0, 1], 1)).toArray(), [2, 5])
assert.deepStrictEqual(math2.subset(A, index([0, 1], [1])).toArray(), [[2], [5]])
// Restore console.warn
warnStub.restore()
})
})

View File

@ -59,7 +59,7 @@ describe('replacer', function () {
const i = new math.Index(new math.Range(0, 10), 2)
const json = '{"mathjs":"Index","dimensions":[' +
'{"mathjs":"Range","start":0,"end":10,"step":1},' +
'{"mathjs":"ImmutableDenseMatrix","data":[2],"size":[1]}' +
'2' +
']}'
assert.deepStrictEqual(JSON.stringify(i), json)
assert.deepStrictEqual(JSON.stringify(i, replacer), json)

View File

@ -494,10 +494,10 @@ describe('DenseMatrix', function () {
assert.deepStrictEqual(m.size(), [3, 3])
assert.deepStrictEqual(m.subset(index(1, 1)), 5)
assert.deepStrictEqual(m.subset(index(new Range(0, 2), new Range(0, 2))).valueOf(), [[1, 2], [4, 5]])
assert.deepStrictEqual(m.subset(index(1, new Range(1, 3))).valueOf(), [[5, 6]])
assert.deepStrictEqual(m.subset(index(0, new Range(1, 3))).valueOf(), [[2, 3]])
assert.deepStrictEqual(m.subset(index(new Range(1, 3), 1)).valueOf(), [[5], [8]])
assert.deepStrictEqual(m.subset(index(new Range(1, 3), 2)).valueOf(), [[6], [9]])
assert.deepStrictEqual(m.subset(index(1, new Range(1, 3))).valueOf(), [5, 6])
assert.deepStrictEqual(m.subset(index(0, new Range(1, 3))).valueOf(), [2, 3])
assert.deepStrictEqual(m.subset(index(new Range(1, 3), 1)).valueOf(), [5, 8])
assert.deepStrictEqual(m.subset(index(new Range(1, 3), 2)).valueOf(), [6, 9])
assert.deepStrictEqual(m.subset(index([0, 1, 2], [1])).valueOf(), [[2], [5], [8]])
// get n-dimensional
@ -506,9 +506,9 @@ describe('DenseMatrix', function () {
assert.deepStrictEqual(m.subset(index(new Range(0, 2), new Range(0, 2), new Range(0, 2))).valueOf(), m.valueOf())
assert.deepStrictEqual(m.subset(index(0, 0, 0)), 1)
assert.deepStrictEqual(m.subset(index(1, 1, 1)).valueOf(), 8)
assert.deepStrictEqual(m.subset(index(1, 1, new Range(0, 2))).valueOf(), [[[7, 8]]])
assert.deepStrictEqual(m.subset(index(1, new Range(0, 2), 1)).valueOf(), [[[6], [8]]])
assert.deepStrictEqual(m.subset(index(new Range(0, 2), 1, 1)).valueOf(), [[[4]], [[8]]])
assert.deepStrictEqual(m.subset(index(1, 1, new Range(0, 2))).valueOf(), [7, 8])
assert.deepStrictEqual(m.subset(index(1, new Range(0, 2), 1)).valueOf(), [6, 8])
assert.deepStrictEqual(m.subset(index(new Range(0, 2), 1, 1)).valueOf(), [4, 8])
})
it('should squeeze the output when index contains a scalar', function () {
@ -518,8 +518,8 @@ describe('DenseMatrix', function () {
m = new DenseMatrix([[1, 2], [3, 4]])
assert.deepStrictEqual(m.subset(index(1, 1)), 4)
assert.deepStrictEqual(m.subset(index(new Range(1, 2), 1)), new DenseMatrix([[4]]))
assert.deepStrictEqual(m.subset(index(1, new Range(1, 2))), new DenseMatrix([[4]]))
assert.deepStrictEqual(m.subset(index(new Range(1, 2), 1)), new DenseMatrix([4]))
assert.deepStrictEqual(m.subset(index(1, new Range(1, 2))), new DenseMatrix([4]))
assert.deepStrictEqual(m.subset(index(new Range(1, 2), new Range(1, 2))), new DenseMatrix([[4]]))
})

View File

@ -255,10 +255,10 @@ describe('ImmutableDenseMatrix', function () {
assert.deepStrictEqual(m.size(), [3, 3])
assert.deepStrictEqual(m.subset(index(1, 1)), 5)
assert.deepStrictEqual(m.subset(index(new Range(0, 2), new Range(0, 2))).valueOf(), [[1, 2], [4, 5]])
assert.deepStrictEqual(m.subset(index(1, new Range(1, 3))).valueOf(), [[5, 6]])
assert.deepStrictEqual(m.subset(index(0, new Range(1, 3))).valueOf(), [[2, 3]])
assert.deepStrictEqual(m.subset(index(new Range(1, 3), 1)).valueOf(), [[5], [8]])
assert.deepStrictEqual(m.subset(index(new Range(1, 3), 2)).valueOf(), [[6], [9]])
assert.deepStrictEqual(m.subset(index(1, new Range(1, 3))).valueOf(), [5, 6])
assert.deepStrictEqual(m.subset(index(0, new Range(1, 3))).valueOf(), [2, 3])
assert.deepStrictEqual(m.subset(index(new Range(1, 3), 1)).valueOf(), [5, 8])
assert.deepStrictEqual(m.subset(index(new Range(1, 3), 2)).valueOf(), [6, 9])
// get n-dimensional
m = new ImmutableDenseMatrix([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
@ -266,9 +266,9 @@ describe('ImmutableDenseMatrix', function () {
assert.deepStrictEqual(m.subset(index(new Range(0, 2), new Range(0, 2), new Range(0, 2))).valueOf(), m.valueOf())
assert.deepStrictEqual(m.subset(index(0, 0, 0)), 1)
assert.deepStrictEqual(m.subset(index(1, 1, 1)).valueOf(), 8)
assert.deepStrictEqual(m.subset(index(1, 1, new Range(0, 2))).valueOf(), [[[7, 8]]])
assert.deepStrictEqual(m.subset(index(1, new Range(0, 2), 1)).valueOf(), [[[6], [8]]])
assert.deepStrictEqual(m.subset(index(new Range(0, 2), 1, 1)).valueOf(), [[[4]], [[8]]])
assert.deepStrictEqual(m.subset(index(1, 1, new Range(0, 2))).valueOf(), [7, 8])
assert.deepStrictEqual(m.subset(index(1, new Range(0, 2), 1)).valueOf(), [6, 8])
assert.deepStrictEqual(m.subset(index(new Range(0, 2), 1, 1)).valueOf(), [4, 8])
})
it('should squeeze the output when index contains a scalar', function () {
@ -278,8 +278,8 @@ describe('ImmutableDenseMatrix', function () {
m = new ImmutableDenseMatrix([[1, 2], [3, 4]])
assert.deepStrictEqual(m.subset(index(1, 1)), 4)
assert.deepStrictEqual(m.subset(index(new Range(1, 2), 1)), new ImmutableDenseMatrix([[4]]))
assert.deepStrictEqual(m.subset(index(1, new Range(1, 2))), new ImmutableDenseMatrix([[4]]))
assert.deepStrictEqual(m.subset(index(new Range(1, 2), 1)), new ImmutableDenseMatrix([4]))
assert.deepStrictEqual(m.subset(index(1, new Range(1, 2))), new ImmutableDenseMatrix([4]))
assert.deepStrictEqual(m.subset(index(new Range(1, 2), new Range(1, 2))), new ImmutableDenseMatrix([[4]]))
})

View File

@ -8,7 +8,7 @@ const ImmutableDenseMatrix = math.ImmutableDenseMatrix
describe('Index', function () {
it('should create an Index', function () {
assert.deepStrictEqual(new Index(0, 2)._dimensions, [new ImmutableDenseMatrix([0]), new ImmutableDenseMatrix([2])])
assert.deepStrictEqual(new Index(0, 2)._dimensions, [0, 2])
assert.deepStrictEqual(new Index(new Range(0, 10))._dimensions, [new Range(0, 10, 1)])
assert.deepStrictEqual(new Index(new Range(0, 10, 2))._dimensions, [new Range(0, 10, 2)])
@ -18,11 +18,11 @@ describe('Index', function () {
])
assert.deepStrictEqual(new Index(new ImmutableDenseMatrix([0, 10]))._dimensions, [new ImmutableDenseMatrix([0, 10])])
assert.deepStrictEqual(new Index([0, 10])._dimensions, [new ImmutableDenseMatrix([0, 10])])
assert.deepStrictEqual(new Index(10)._dimensions, [new ImmutableDenseMatrix([10])])
assert.deepStrictEqual(new Index(10)._dimensions, [10])
})
it('should create an Index from bigints', function () {
assert.deepStrictEqual(new Index(0n, 2n)._dimensions, [new ImmutableDenseMatrix([0]), new ImmutableDenseMatrix([2])])
assert.deepStrictEqual(new Index(0n, 2n)._dimensions, [0, 2])
assert.deepStrictEqual(new Index(new Range(0n, 10n))._dimensions, [new Range(0, 10, 1)])
assert.deepStrictEqual(new Index(new Range(0n, 10n, 2))._dimensions, [new Range(0, 10, 2)])
@ -105,11 +105,11 @@ describe('Index', function () {
assert.strictEqual(new Index(2, 5, 2).isScalar(), true)
assert.strictEqual(new Index(2).isScalar(), true)
assert.strictEqual(new Index([0, 1, 2], 2).isScalar(), false)
assert.strictEqual(new Index([3], [2]).isScalar(), true)
assert.strictEqual(new Index([3], [2]).isScalar(), false)
assert.strictEqual(new Index([0, 1, 2], [2]).isScalar(), false)
assert.strictEqual(new Index(new Range(2, 10)).isScalar(), false)
assert.strictEqual(new Index(new ImmutableDenseMatrix([2, 10])).isScalar(), false)
assert.strictEqual(new Index(new ImmutableDenseMatrix([2])).isScalar(), true)
assert.strictEqual(new Index(new ImmutableDenseMatrix([2])).isScalar(), false)
assert.strictEqual(new Index(2, new Range(0, 4), 2).isScalar(), false)
assert.strictEqual(new Index(2, new ImmutableDenseMatrix([0, 4]), 2).isScalar(), false)
assert.strictEqual(new Index(new Range(0, 2), new Range(0, 4)).isScalar(), false)
@ -119,8 +119,8 @@ describe('Index', function () {
})
it('should clone an Index', function () {
const index1 = new Index(2, new Range(0, 4), new ImmutableDenseMatrix([0, 2]))
const index2 = index1.clone(0)
const index1 = new Index([2], new Range(0, 4), new ImmutableDenseMatrix([0, 2]))
const index2 = index1.clone()
assert.deepStrictEqual(index1, index2)
assert.notStrictEqual(index1, index2)
@ -131,9 +131,9 @@ describe('Index', function () {
it('should stringify an index', function () {
assert.strictEqual(new Index().toString(), '[]')
assert.strictEqual(new Index(2, 3).toString(), '[[2], [3]]')
assert.strictEqual(new Index(2, 3, 1).toString(), '[[2], [3], [1]]')
assert.strictEqual(new Index(2, new Range(0, 3)).toString(), '[[2], 0:3]')
assert.strictEqual(new Index(2, 3).toString(), '[2, 3]')
assert.strictEqual(new Index(2, 3, 1).toString(), '[2, 3, 1]')
assert.strictEqual(new Index(2, new Range(0, 3)).toString(), '[2, 0:3]')
assert.strictEqual(new Index(new Range(0, 6, 2)).toString(), '[0:2:6]')
assert.strictEqual(new Index(new ImmutableDenseMatrix([0, 6, 2])).toString(), '[[0, 6, 2]]')
assert.deepStrictEqual(new Index('property').toString(), '["property"]')
@ -145,7 +145,7 @@ describe('Index', function () {
mathjs: 'Index',
dimensions: [
new Range(0, 10, 1),
new ImmutableDenseMatrix([2]),
2,
new ImmutableDenseMatrix([1, 2, 3])
]
})
@ -155,7 +155,7 @@ describe('Index', function () {
const json = {
dimensions: [
new Range(0, 10, 1),
new ImmutableDenseMatrix([2]),
2,
new ImmutableDenseMatrix([1, 2, 3])
]
}
@ -169,8 +169,8 @@ describe('Index', function () {
it('should get the range for a given dimension', function () {
const index = new Index(2, new Range(0, 8, 2), new Range(3, -1, -1), new ImmutableDenseMatrix([1, 2, 3]))
assert(index.dimension(0) instanceof ImmutableDenseMatrix)
assert.deepStrictEqual(index.dimension(0), new ImmutableDenseMatrix([2]))
assert(Number.isInteger(index.dimension(0)))
assert.deepStrictEqual(index.dimension(0), 2)
assert(index.dimension(1) instanceof Range)
assert.deepStrictEqual(index.dimension(1), new Range(0, 8, 2))
@ -197,7 +197,7 @@ describe('Index', function () {
})
assert.deepStrictEqual(log, [
{ dimension: new ImmutableDenseMatrix([2]), index: 0 },
{ dimension: 2, index: 0 },
{ dimension: new Range(0, 8, 2), index: 1 },
{ dimension: new Range(3, -1, -1), index: 2 },
{ dimension: new ImmutableDenseMatrix([1, 2, 3]), index: 3 }
@ -229,12 +229,12 @@ describe('Index', function () {
assert.deepStrictEqual(new Index(new Range(2, 5), new Range(0, 8, 2), 2, new ImmutableDenseMatrix([1, 2])).toArray(), [
[2, 3, 4],
[0, 2, 4, 6],
[2],
2,
[1, 2]
])
assert.deepStrictEqual(new Index(2, new Range(0, 8, 2), new Range(3, -1, -1), new Range(2, 4, 0), [1, 2]).toArray(), [
[2],
2,
[0, 2, 4, 6],
[3, 2, 1, 0],
[],
@ -246,7 +246,7 @@ describe('Index', function () {
it('valueOf should return the expanded array', function () {
assert.deepStrictEqual(new Index(2, new Range(0, 8, 2), new Range(3, -1, -1), [1, 2], new ImmutableDenseMatrix([3, 4])).valueOf(), [
[2],
2,
[0, 2, 4, 6],
[3, 2, 1, 0],
[1, 2],

View File

@ -3,7 +3,6 @@ import assert from 'assert'
import math from '../../../../../src/defaultInstance.js'
const Range = math.Range
const ImmutableDenseMatrix = math.ImmutableDenseMatrix
describe('index', function () {
it('should create an index', function () {
@ -22,13 +21,13 @@ describe('index', function () {
it('should create an index from bignumbers (downgrades to numbers)', function () {
const index = math.index(new Range(math.bignumber(2), math.bignumber(6)), math.bignumber(3))
assert.ok(index instanceof math.Index)
assert.deepStrictEqual(index._dimensions, [new Range(2, 6, 1), new ImmutableDenseMatrix([3])])
assert.deepStrictEqual(index._dimensions, [new Range(2, 6, 1), 3])
})
it('should create an index from bigints (downgrades to numbers)', function () {
const index = math.index(new Range(2n, 6n), 3n)
assert.ok(index instanceof math.Index)
assert.deepStrictEqual(index._dimensions, [new Range(2, 6, 1), new ImmutableDenseMatrix([3])])
assert.deepStrictEqual(index._dimensions, [new Range(2, 6, 1), 3])
})
it('should LaTeX index', function () {