From d367a85c8b9ef526a7ffef1a6ad119033487071f Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 19 Jun 2025 23:59:07 +0530 Subject: [PATCH 1/3] Specialize lmul!/rmul! for adjoint/transpose --- src/adjtrans.jl | 3 +++ test/adjtrans.jl | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/adjtrans.jl b/src/adjtrans.jl index c4de7dae..affc952b 100644 --- a/src/adjtrans.jl +++ b/src/adjtrans.jl @@ -513,6 +513,9 @@ function _dot_nonrecursive(u, v) end end +rmul!(X::AdjOrTrans, s::Number) = (rmul!(parent(X), s); X) +lmul!(s::Number, X::AdjOrTrans) = (lmul!(s, parent(X)); X) + # Adjoint/Transpose-vector * vector *(u::AdjointAbsVec{<:Number}, v::AbstractVector{<:Number}) = dot(u.parent, v) *(u::TransposeAbsVec{T}, v::AbstractVector{T}) where {T<:Real} = dot(u.parent, v) diff --git a/test/adjtrans.jl b/test/adjtrans.jl index 43868162..1ffd3319 100644 --- a/test/adjtrans.jl +++ b/test/adjtrans.jl @@ -809,4 +809,14 @@ end end end +@testset "lmul!/rmul! by numbers" begin + for A in (rand(4, 4), rand(ComplexF64,4,4), + fill([1 2; 3 4], 4, 4)) + B = copy(A) + @test lmul!(2, B) == 2 * A + B .= A + @test rmul!(B, 2) == A * 2 + end +end + end # module TestAdjointTranspose From a359c7cb983a1dd01befb08f392802c9af2b5022 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 20 Jun 2025 00:10:43 +0530 Subject: [PATCH 2/3] Add complex tests --- src/adjtrans.jl | 4 ++-- test/adjtrans.jl | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/adjtrans.jl b/src/adjtrans.jl index affc952b..a13e98d6 100644 --- a/src/adjtrans.jl +++ b/src/adjtrans.jl @@ -513,8 +513,8 @@ function _dot_nonrecursive(u, v) end end -rmul!(X::AdjOrTrans, s::Number) = (rmul!(parent(X), s); X) -lmul!(s::Number, X::AdjOrTrans) = (lmul!(s, parent(X)); X) +rmul!(X::AdjOrTrans, s::Number) = (lmul!(wrapperop(X)(s), parent(X)); X) +lmul!(s::Number, X::AdjOrTrans) = (rmul!(parent(X), wrapperop(X)(s)); X) # Adjoint/Transpose-vector * vector *(u::AdjointAbsVec{<:Number}, v::AbstractVector{<:Number}) = dot(u.parent, v) diff --git a/test/adjtrans.jl b/test/adjtrans.jl index 1ffd3319..9c926bc2 100644 --- a/test/adjtrans.jl +++ b/test/adjtrans.jl @@ -810,12 +810,21 @@ end end @testset "lmul!/rmul! by numbers" begin - for A in (rand(4, 4), rand(ComplexF64,4,4), + @testset "$(eltype(A))" for A in (rand(4, 4), rand(ComplexF64,4,4), fill([1 2; 3 4], 4, 4)) B = copy(A) - @test lmul!(2, B) == 2 * A - B .= A - @test rmul!(B, 2) == A * 2 + @testset for op in (transpose, adjoint) + A .= B + @test lmul!(2, op(A)) == 2 * op(B) + A .= B + @test rmul!(op(A), 2) == op(B) * 2 + if eltype(A) <: Complex + A .= B + @test lmul!(-2im, op(A)) == -2im * op(B) + A .= B + @test rmul!(op(A), -2im) == op(B) * -2im + end + end end end From a462e5a8f41bc27b7671b485c205348e2ac63c4a Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sat, 21 Jun 2025 22:44:00 +0530 Subject: [PATCH 3/3] Restrict to real/complex numbers --- src/adjtrans.jl | 5 +++-- test/adjtrans.jl | 11 ++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/adjtrans.jl b/src/adjtrans.jl index a13e98d6..51f17be8 100644 --- a/src/adjtrans.jl +++ b/src/adjtrans.jl @@ -513,8 +513,9 @@ function _dot_nonrecursive(u, v) end end -rmul!(X::AdjOrTrans, s::Number) = (lmul!(wrapperop(X)(s), parent(X)); X) -lmul!(s::Number, X::AdjOrTrans) = (rmul!(parent(X), wrapperop(X)(s)); X) +# we use (n A^T) = (A n)^T, which holds if the product of n and the elements of A is commutative +rmul!(X::AdjOrTrans{<:Union{Real,Complex}}, s::Union{Real,Complex}) = (lmul!(wrapperop(X)(s), parent(X)); X) +lmul!(s::Union{Real,Complex}, X::AdjOrTrans{<:Union{Real,Complex}}) = (rmul!(parent(X), wrapperop(X)(s)); X) # Adjoint/Transpose-vector * vector *(u::AdjointAbsVec{<:Number}, v::AbstractVector{<:Number}) = dot(u.parent, v) diff --git a/test/adjtrans.jl b/test/adjtrans.jl index 9c926bc2..85a17b3d 100644 --- a/test/adjtrans.jl +++ b/test/adjtrans.jl @@ -12,6 +12,7 @@ isdefined(Main, :LinearAlgebraTestHelpers) || Base.include(Main, TESTHELPERS) using Main.LinearAlgebraTestHelpers.OffsetArrays using Main.LinearAlgebraTestHelpers.ImmutableArrays +using Main.LinearAlgebraTestHelpers.Quaternions @testset "Adjoint and Transpose inner constructor basics" begin intvec, intmat = [1, 2], [1 2; 3 4] @@ -811,7 +812,8 @@ end @testset "lmul!/rmul! by numbers" begin @testset "$(eltype(A))" for A in (rand(4, 4), rand(ComplexF64,4,4), - fill([1 2; 3 4], 4, 4)) + fill([1 2; 3 4], 4, 4), + fill(Quaternion(1,2,3,4), 4, 4)) B = copy(A) @testset for op in (transpose, adjoint) A .= B @@ -824,6 +826,13 @@ end A .= B @test rmul!(op(A), -2im) == op(B) * -2im end + if eltype(A) <: Quaternion + A .= B + q = Quaternion(0,1,4,7) + @test lmul!(q, op(A)) == q * op(B) + A .= B + @test rmul!(op(A), q) == op(B) * q + end end end end