I personally would be very confused if using *
between two objects that claim to be matrices did anything other than matrix multiplication.
While in some contexts the term matrix is used loosely to talk about "bags" of numbers arranged in rows and columns, there's usually decent naming alternatives for that (ie Numpy's ndarray
). However, when matrices are used in linear algebra, there are no good naming alternatives. The term "matrix" is already defined to mean something very specific. For that reason, I would strongly encourage naming the type anything other than Matrix
if the *
operator is going to do something other than matrix multiplication.
I do like the idea of keeping separate types, like Matrix
and ShapedArray
to keep the conceptual distinction clear, and have the *
operator do different things based on the type.
Also, Vector
is an interesting case, as one would need to support both row-vectors and column-vectors, and at that point it's just a special case of Matrix
. IIRC Numpy sidesteps this issue by calling everything an array.
We're totally looking at this from different perspectives, but I wouldn't have considered whether the operation involves doing some computations element-wise or not is not as an important distinction when dealing with matrices.
To me, the distinction is whether or not the operation is (traditionally) defined for matrices:
- Addition, subtraction and (matrix) multiplication are well defined, so I'd use the plain operators
+
, -
and *
, because that's the default meaning of addition, subtraction and multiplication when dealing with matrices.
- Element-wise multiplication, division, exponentials... are the "extraneous" operations for matrices (that only make sense when treating matrices as bags of numbers), so I'd use different operators for those:
.*
, ./
, .^
(for example).
So having +
, -
and *
is already consistent in some way, even though the first two do "element-wise" computations and the third doesn't.
This would change if the objects had a different name that didn't imply that the object is a matrix (ie ShapedArray
). Then I might have defaulted to *
for element-wise multiplication and used something different (maybe even a more explicit matmul()
method) for matrix multiplication.
One downside of LinearOperator
is that matrices are often used to perform non-linear operations. Notably in graphics, for example: perspective projection matrices.
It feels wrong to write:
let projectionMatrix: LinearOperator4x4
Because the perspective projection is a non-linear transformation in 3D (it's still a linear operator on the 4D vector, but in this case you're only interested in the first 3 coordinates, which makes the LinearOperator
naming a bit awkward).