I had a need for basic matrix multiplication for a CeTZ diagram, so:
#let matvec(A, v) = A.map(Ai => Ai.zip(v).map(array.product).sum())
#let transpose(A) = range(A.first().len()).map(j => A.map(Ai => Ai.at(j)))
#let vecmat(v, A) = matvec(transpose(A), v)
#let matmat(A, B) = A.map(Ai => vecmat(Ai, B))
// Tests
#assert(matvec(((1, 2), (3, 4)), (2, 3)) == (8, 18))
#assert(transpose(((1, 2), (3, 4), (5, 6))) == ((1, 3, 5), (2, 4, 6)))
#assert(vecmat((2, 3), ((1, 2), (3, 4))) == (11, 16))
#let A = ((1, 2), (3, 4))
#let B = ((1, 3, 4), (2, 1, 8))
#import "@preview/cetz:0.3.2": util.matrix
#assert(matmat(A, B) == matrix.mul-mat(A, B))
Maybe it will be useful to someone else… As you can see with the last test, I then realized I could just use the CeTZ util functions instead But sometimes it might make sense to avoid that dependency.