Negative false no longer acts as a strong zero

Thanks for bringing this to the discussion @lance_xwq !
Here’s a follow up: How consistent should the NaN*false behaviour be treated? As an example:

julia> using LinearAlgebra

julia> x, A, y = fill(NaN32, 2), fill(NaN32, 2, 2), fill(NaN32, 2)
(Float32[NaN, NaN], Float32[NaN NaN; NaN NaN], Float32[NaN, NaN])

julia> mul!(y, A, x, false, false) # stops the NaN propagation
2-element Vector{Float32}:
 0.0
 0.0

julia> x, A, y = fill(NaN16, 2), fill(NaN16, 2, 2), fill(NaN16, 2)
(Float16[NaN, NaN], Float16[NaN NaN; NaN NaN], Float16[NaN, NaN])

julia> mul!(y, A, x, false, false) # does NOT stop the NaN's
2-element Vector{Float16}:
 NaN
 NaN

julia> X, A, Y = fill(NaN16, 2, 2), fill(NaN16, 2, 2), fill(NaN16, 2, 2)
(Float16[NaN NaN; NaN NaN], Float16[NaN NaN; NaN NaN], Float16[NaN NaN; NaN NaN])

julia> mul!(Y, A, X, false, false) # however, matmatmul does
2×2 Matrix{Float16}:
 0.0  0.0
 0.0  0.0

Is this worth an issue?

Yes, please file an issue in LinearAlgebra.jl

Yes. This is a bug.

The implementation of __generic_matvecmul!(::typeof(identity), C::AbstractVector, A::AbstractVecOrMat, B::AbstractVector, alpha::Number, beta::Number) appears to be plain wrong.

The issue is that x * (NaN * false) != (x * NaN) * false for x=NaN and the code re-associates the latter into the former, in order to loop-hoist one multiplication.

This messes up all matmuls with non-associative multiplication that are not saved by specialized implementations.

The appropriate thing is to have a check for beta == false, because that allows us to also save a lot of time. I would go even further and have a check for iszero(beta) and iszero(alpha), and document that mul!(C, A, B, alpha, beta) is permitted (but not obligated) to treat vanishing alpha, beta as strong zeros.

Also, false*missing === missing is silly.

2 Likes