Skip to content

Conversation

@dvd101x
Copy link
Collaborator

@dvd101x dvd101x commented Dec 9, 2025

Hi, this is the proof of concept discussed at #3537

It's more inline with what Jos and Glen suggested as my initial idea was flawed. This PR only shows two main changes.

  • refactors matAlgo14xDs to use it's DenseMatrix map method and includes a hack to make the matrix creation faster.
  • makes a new matAlgo15xAs to map arrays that can be jagged or non homogeneous and there is no need to convert to matrix and then back to array.

My expectation is that all operations between Matrix|Array and scalar should be faster.

The new functionality is that it's possible to do stuff like

math.add([1,[2,3]], 1)

math.config({matrix:"Array"})
math.evaluate("[1,[2,3]] + 1")

@josdejong
Copy link
Owner

Nice! This actually simplifies the code since you can "just" use the map function.

What are the next steps? Add some unit tests for jagged inputs, update docs, and see what the performance impact is?

@dvd101x
Copy link
Collaborator Author

dvd101x commented Dec 11, 2025

Yes exactly, I will proceed with the next steps in the following weeks.

Maybe later I will come back to broadcasting jagged arrays.

Copy link
Collaborator

@gwhitney gwhitney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent, this is shaping up. A few more things to consider.

const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
const matAlgo15xAs = createMatAlgo15xAs()
const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This concat is not actually a dependency of matrixAlgorithmSuite. Since you are here, please remove it. Most likely that will allow concat to be removed as a dependency altogether.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! just addressed this.

- `Array`, a regular JavaScript array. A multidimensional array can be created
by nesting arrays.
by nesting arrays. If the elements of an array are of the same size, it is a
rectangular array. If the elements are all arrays but not of the same size,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should say that these words "rectangular", "jagged", "heterogeneous" are the terminology mathjs will use -- it's not like these are universally standard terms.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, included a brief explanation of the terminology that would be used. I'm not familiar weather or not these are known, but took references of wikipedia to define the names.

(vertically) and `y` as _row_ (horizontally).

## Not rectangular arrays

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should that be "Non-rectangular arrays" ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! just addressed this.

[[1, 2], 3] // heterogeneous
```

These types of arrays can't be converted to a matrix, but many operations are available for them.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Say "Jagged and heterogeneous arrays" instead of "These types of arrays" because one of the examples you just showed was rectangular and can be converted to a matrix.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the thorough read. Just addressed this issue.

```js
math.size([[1, 2], [3]]) // [2, 2]
```
The process of validation for rectangularity is expensive and this provides a fast way of working with nested arrays of many kinds.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The antecedent of "this" in "this provides" is unclear. Please clarify exactly what provides a fast way of working with nested arrays.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! just addressed this issue.

const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
const matAlgo15xAs = createMatAlgo15xAs()
const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concat removal here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! just addressed this issue.

const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
const matAlgo15xAs = createMatAlgo15xAs()
const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And remove concat here too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! just addressed this issue. Please review

// callback
cf = typed.find(callback, [dt, dt])
cf = typed.find(callback, [adt, adt])
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize that you didn't create this algorithm for dealing with the case when the matrix is of a known datatype, but it doesn't seem correct. For example, consider the case of a number matrix and adding a bignumber. Normally a number plus a bignumber is a bignumber. Since addition distributes over the entries of a matrix, I would expect a matrix of numbers, plus a bignumber, to produce a matrix of bignumbers. But by the above logic, instead the bignumber will be converted to a number (possibly losing precision, and since this is an explicit call to convert, there will be no warning), and you will end up with a matrix of numbers. That seems buggy, especially the potential un-warned loss of precision. If you agree, I would personally be fine folding in a fix to this concern (as long as it is not breaking) into this PR. However @josdejong may disagree, and want addressing this potential issue to be in a separate PR. If so, or if it is breaking, please file an issue for the type-handling of matrix-scalar operations, noting whether it is breaking. Thanks!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this I tried to maintain behavior as is. Maybe this could be addressed in a different PR.

Currently add a Matrix of numbers to a big number throws an error but tries to convert the bigNumber to number.

math.add(math.matrix([1], "dense", "number"), math.bignumber(1))   // Uncaught Error: Cannot convert 1 to number

I agree it's not right and the same goes for map where it assumes that if the input is of a certain type the output will be as well.

math.matrix([1], "dense", "number").map(x => math.add(x, math.bignumber(1)))  // returns a matrix of datatype "number" that contains bignumbers.

I don't see an easy fix about the types for when a map accepts a callback, other than checking the output.

But for operators maybe there is a way specific to each operator and datatype or a database of two type inputs equals a type output.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be wrong, I will validate again if I'm changing the behavior.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, well in the meantime I have filed #3612; you can address it or not in this PR as you and Jos see fit, but we should definitely fix it at some point in the not-too-distant future since it is definitely a bug.

Copy link
Owner

@josdejong josdejong Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for bringing this issue up Glen, we should indeed fix this behavior. But best to do that in a separate PR I think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I did validate and I think the original behavior is maintained, please let me know if I'm mistaken.

export const createMatAlgo15xAs = /* #__PURE__ */ factory(name, dependencies, () => {
/**
* Iterates over Array items and invokes the callback function f(Aij..z, b).
* Callback function invoked MxN times.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since in this case the Array may quite explicitly not be rectangular, saying "MxN" is a bit misleading. Consider changing to something like "Callback function invoked once for each entry of A."

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! just addressed this issue. Please review

@dvd101x
Copy link
Collaborator Author

dvd101x commented Dec 17, 2025

After including benchmarks, there is a big benefit of not converting arrays to matrices and small benefit of using the DenseMatrix.map method.

image

@dvd101x
Copy link
Collaborator Author

dvd101x commented Dec 20, 2025

Hi, all comments are addressed either in code or as a reply. This is ready for review again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants