Is matrix multiplication not associative?

I agree.

I’ve learned that.

The following is an example. Presumably sm is a more fundamental operation than s_conj_m.
And transpose (rather than ') corresponds to sm. (That’s why in some sense I think transpose is more fundamental).

function sm(p, x)       return sum(p .* x)       end
function s_conj_m(p, x) return sum(conj(p) .* x) end
import LinearAlgebra.tr as tr
A = rand(ComplexF64, 3, 2)
B = rand(ComplexF64, 3, 2)
# ✅ The following LHS == RHS
LHS, RHS = sm(A, B), tr(transpose(A) * B)
@assert LHS == RHS "might due to tolerance";

# ✅ The following LHS == RHS
LHS, RHS = s_conj_m(A, B), tr(A' * B)
@assert LHS == RHS "might due to tolerance";

You seem to imply that, conceptually, adjoint == conj ∘ transpose, thus transpose is simpler and more fundamental when your arrays are real. I think @stevengj is arguing that you should reframe this as follows:

Vector space Linear algebra concept Implementation for arrays
Real adjoint transpose
Complex adjoint conj ∘ transpose

In other words, you should let go of the idea that adjoint always implies conjugation. Rather, adjoint is the linear algebra concept that transpose accomplishes on a real-valued array, and conj ∘ transpose accomplishes on a complex-valued array.

The beauty of multiple dispatch is that function names can live on the conceptual level while compiling down to code that’s specific to the case at hand. You don’t have to worry about redundant conj calls under the hood when using adjoint on real vectors, the same way you don’t have to worry about hitting the complex code path applicable to exp(2.0 + 2.0im) when calling exp(2.0).

6 Likes

This makes sense, which is reassuring.