Questions about Goals and Future Direction for Matrix Multiplication #584
Replies: 5 comments 2 replies
-
|
Beta Was this translation helpful? Give feedback.
-
Thanks for the detailed analysis. Looks like some of the types need to be cleaned up. I was doing some profiling of a benchmark that used Color.js on the weekend and found that
I agree that |
Beta Was this translation helpful? Give feedback.
-
I found some benchmarks on matrix math. It only benchmarks element-wise addition of matrices but could be useful in structuring future specialized functions. |
Beta Was this translation helpful? Give feedback.
-
I took a look at the CSS 4 Editor's Draft's Color Conversion Code section My understanding of the linked article on column-major ordering, the wikipedia article on matrix multiplication, Matrices are 2 dimensional rectangular collections and we're only interested in collections of numbers. So matrices can be described as tables where each cell in the table can contain a collected number.
MathematicallyMathematically, matrices can be accessed or manipulated by element, row, or column. All 3 are equally important and each can be preferred depending how the matrix will be used. The trouble comes with how to use vectors and matrices together. A matrix can be described as made up of both rows and columns at the same time. But a vector is 1 dimensional and can only be described as either a row or column. The transpose operator easily changes a row to a column and column to a row. So for consistency it needs to be stated if a vector is by default a representation of a row or a column. This is where row-major order and column-major order come in. The -major order states the default format of vectors. Row vectors and column vectors each require a specific multiplication order to be equivalent:
This importance of multiplication ordering especially applies to successive multiplications, but Computer ImplementationComputer implementations can extend the notational representation of row-major order and column-major order and actually use the -major ordering as a low level structure of the data in memory. There are performance reason and optimizations that can be made for how the data is accessed and stored in-memory, but we can ignore any of that since In But plain arrays in js don't contain enough information to say if they represent row, or column vectors. To absolutely specify the type requires representing the vector as a nested array. So, to force the array
|
Beta Was this translation helpful? Give feedback.
-
This would seem to be the far more likely need in this library, essentially a vector dot. If you want to multiply it some other way, matrixMultiply would likely not be the choice, and some generic multiplication function could be used instead.
The above is essentially what Numpy does (a scientific library in Python). The library is row major by default, but >>> import numpy as np
>>> np.matmul([1, 2, 3], [[1, 2, 3], [4, 5, 6], [7, 8, 9]])
array([30, 36, 42])
>>> np.matmul([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [1, 2, 3])
array([14, 32, 50])
>>> np.matmul([1, 2, 3], [4, 5, 6])
32 I think they don't allow scalar operations in this function as a scalar is obviously not a matrix, but we could do whatever we want. This is just my thoughts. I'm familiar with this approach, so I'm biased, but I'll adapt to whatever. |
Beta Was this translation helpful? Give feedback.
-
Hello Everyone,
I worked on some tests and type fixes for
multiplyMatrices.js
and wanted to share some edge cases and ask about future implementation goals and priorities.I previously mentioned I was going to open an issue to outline what I found while working on tests and types, but there's enough to talk about that I thought it would be better to try opening a discussion instead.
After some consensus is reached, I'll breakdown the decisions into individual issues and more focused pull requests.
Reference Implementation
I'm testing possible implementation details and running tests with an adaptation of
color.js
'smultiplyMatrices.js
in a private typescript project. The typescript code is available in a github gist (hover to see when last updated).I already have testing and code coverage setup for that project so it's easier for me to play around with things there. And I plan on porting the relevant tests to
hTest
andcolor.js
in future.The private project also includes tests for simulating prota-/deutera-/trita-nopia on sRGB colors using
color.js
and compares reference results from DaltonLens-Python. So I've got quite a lot of tests already. (After making the simulation work with non-sRGB colors I plan on open sourcing it, but that is a long way off and not part of this discussion.)Observations
Priority
What I gleaned from a previous version of the API docs is that
multiplyMatrices.js
isn't focused on ensuring the correctness of every arbitrary matrix product. The priority seemed to be 1) correctness for common use cases, and 2) performance to reduce the overhead of the frequent transformation of coordinates from one Color Space to another.Arguments & Return Types
In
color.js
(as of 2024-08-10) there are 25 non-test uses ofmultiplyMatrices()
, roughly the types work out to be:[number number number]
[number number number][]
number[]
number[][]
any
any
seems to be 3-Array:number[]
seems to be 1×3:number[]
seems to be 3×1:number[]
seems to be 3-Array:number[][]
seems to be 3×3:I did a rough scan of the code to get these numbers, they aren't definitive.
Current
multiplyMatrices
Implementation NotesAM
is a row-ordered matrix so therow
argument of the first map will always be an Array. (Code Coverage never reached this code in my tests.)(col[i] || 0)
to handle possible undefined access of thecol
variable. This is guaranteed to be an invalid product if the|| 0
is used.Another possible choice is using{See comment below.}NaN
instead of0
.for
loop on line 72 loops until the length of the row in theA
matrix, the returned value's shape is always constrained byA
. Iterating based oncol.length
, and using(row[i] || 0)
, returns the same correct answers and is also a reasonable choice.TransformationMatrix
(3×3) type and use it along with a non-nullCoords
type to specify precise return types.A
's rows orB
's columns. Which means eitherA
orB
is guaranteed to constrain the final returned shape.multiplyMatrices
returns a[number]
result.[[]]
,[[1]]
,[[1],[2],[3]]
, etc.)number[][]
type but are mathematically equivalent to vectors. Notationally1
is the same as[1]
is the same as[[1]]
, but they are not the same computationally. Computational equivalence requires properly packing and unpacking these nested arrays..length
onundefined
). We could throw our own errors, attempt to return an obviously incorrect value (but type/shape correct) result, or leave as is for performance reasonsB
is[]
then calling map on it will assign[]
toBM
, rather than the expected[[]]
.m
is actually assigned the value ofn
, whenA
is anumber[]
.The Future
My Next Steps Recommendation
TransformationMatrix
type and use it and/or non-nullCoords
type to highlight "happy path" usages ofmultiplyMatrices
(ref notes-4,5)Needs Discussion
0
/NaN
) in the proper return shape) [related to notes-9,2,3]Coords
includesnull
andnumber
s as acceptable values, shouldmultiplyMatrices
accept them as well? (I'm not sure howmultiplyMatrices
could handle this correctly for all cases. So it's probably better for each Color Space to resolvenull
values.) [related to notes-4,2,9]For Now
Thanks for reading and let know what you think. I'll probably focus on adding more test in the near future. But I'm open to hearing other perspectives and recommendations.
Beta Was this translation helpful? Give feedback.
All reactions