Skip to content

Commit 3ee419b

Browse files
authored
allow direct multiplication with out-of-place transpose/adjoint FunctionMaps (#83)
1 parent 039e35f commit 3ee419b

File tree

4 files changed

+93
-11
lines changed

4 files changed

+93
-11
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ os:
88

99
julia:
1010
- 1.0
11-
- 1.3
11+
- 1
1212
- nightly
1313

1414
matrix:

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "LinearMaps"
22
uuid = "7a12625a-238d-50fd-b39a-03d52299707e"
3-
version = "2.6"
3+
version = "2.6.1"
44

55
[deps]
66
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

src/functionmap.jl

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,64 @@ function Base.:(*)(A::FunctionMap, x::AbstractVector)
4848
end
4949
return y
5050
end
51+
function Base.:(*)(A::AdjointMap{<:Any,<:FunctionMap}, x::AbstractVector)
52+
Afun = A.lmap
53+
ishermitian(Afun) && return Afun*x
54+
length(x) == size(A, 2) || throw(DimensionMismatch())
55+
if Afun.fc !== nothing
56+
if ismutating(Afun)
57+
y = similar(x, promote_type(eltype(A), eltype(x)), size(A, 1))
58+
Afun.fc(y, x)
59+
else
60+
y = Afun.fc(x)
61+
end
62+
return y
63+
elseif issymmetric(Afun) # but !isreal(A), Afun.f can be used
64+
x = conj(x)
65+
if ismutating(Afun)
66+
y = similar(x, promote_type(eltype(A), eltype(x)), size(A, 1))
67+
Afun.f(y, x)
68+
else
69+
y = Afun.f(x)
70+
end
71+
conj!(y)
72+
return y
73+
else
74+
error("adjoint not implemented for $A")
75+
end
76+
end
77+
function Base.:(*)(A::TransposeMap{<:Any,<:FunctionMap}, x::AbstractVector)
78+
Afun = A.lmap
79+
(issymmetric(Afun) || (isreal(A) && ishermitian(Afun))) && return Afun*x
80+
length(x) == size(A, 2) || throw(DimensionMismatch())
81+
if Afun.fc !== nothing
82+
if !isreal(A)
83+
x = conj(x)
84+
end
85+
if ismutating(Afun)
86+
y = similar(x, promote_type(eltype(A), eltype(x)), size(A, 1))
87+
Afun.fc(y, x)
88+
else
89+
y = Afun.fc(x)
90+
end
91+
if !isreal(A)
92+
conj!(y)
93+
end
94+
return y
95+
elseif ishermitian(Afun) # but !isreal(A), Afun.f can be used
96+
x = conj(x)
97+
if ismutating(Afun)
98+
y = similar(x, promote_type(eltype(A), eltype(x)), size(A, 1))
99+
Afun.f(y, x)
100+
else
101+
y = Afun.f(x)
102+
end
103+
conj!(y)
104+
return y
105+
else
106+
error("transpose not implemented for $A")
107+
end
108+
end
51109

52110
function A_mul_B!(y::AbstractVector, A::FunctionMap, x::AbstractVector)
53111
(length(x) == A.N && length(y) == A.M) || throw(DimensionMismatch("A_mul_B!"))
@@ -78,7 +136,7 @@ function At_mul_B!(y::AbstractVector, A::FunctionMap, x::AbstractVector)
78136
end
79137

80138
function Ac_mul_B!(y::AbstractVector, A::FunctionMap, x::AbstractVector)
81-
(ishermitian(A) || (isreal(A) && issymmetric(A))) && return A_mul_B!(y, A, x)
139+
ishermitian(A) && return A_mul_B!(y, A, x)
82140
(length(x) == A.M && length(y) == A.N) || throw(DimensionMismatch("Ac_mul_B!"))
83141
if A.fc !== nothing
84142
ismutating(A) ? A.fc(y, x) : copyto!(y, A.fc(x))

test/functionmap.jl

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ using Test, LinearMaps, LinearAlgebra
5656
@test @inferred mul!(similar(v), CS!, v) == cv
5757
@test_throws ErrorException CS!'v
5858
@test_throws ErrorException adjoint(CS!) * v
59+
@test_throws ErrorException mul!(similar(v), CS!', v)
60+
@test_throws ErrorException mul!(similar(v), transpose(CS!), v)
5961
CS! = LinearMap{ComplexF64}(cumsum!, (y, x) -> (copyto!(y, x); reverse!(y); cumsum!(y, y)), 10; ismutating=true)
6062
@inferred adjoint(CS!)
6163
@test @inferred LinearMaps.ismutating(CS!)
@@ -73,15 +75,37 @@ using Test, LinearMaps, LinearAlgebra
7375
@test @inferred transpose(2 * L) * v 2 * v
7476
L = @inferred LinearMap{ComplexF64}(x -> x, x -> x, 10)
7577
v = rand(ComplexF64, 10)
78+
w = similar(v)
7679
@test @inferred (2 * L)' * v 2 * v
7780
@test @inferred transpose(2 * L) * v 2 * v
7881

79-
L = LinearMap{ComplexF64}(identity, 10; issymmetric=true)
80-
@test L * v == v
81-
@test adjoint(L) * v == v
82-
@test transpose(L) * v == v
83-
L = LinearMap{ComplexF64}(identity, 10; ishermitian=true)
84-
@test L * v == v
85-
@test adjoint(L) * v == v
86-
@test transpose(L) * v == v
82+
A = rand(ComplexF64, 10, 10)
83+
L = LinearMap{ComplexF64}(x -> A*x, 10)
84+
@test L * v == A * v == mul!(w, L, v)
85+
L = LinearMap{ComplexF64}((y, x) -> mul!(y, A, x), 10)
86+
@test L * v == A * v == mul!(w, L, v)
87+
L = LinearMap{ComplexF64}((y, x) -> mul!(y, A, x), (y, x) -> mul!(y, A', x), 10)
88+
@test L * v == A * v == mul!(w, L, v)
89+
@test adjoint(L) * v A'v mul!(w, L', v)
90+
@test transpose(L) * v transpose(A)*v mul!(w, transpose(L), v)
91+
92+
A = Symmetric(rand(ComplexF64, 10, 10))
93+
L = LinearMap{ComplexF64}(x -> A*x, 10; issymmetric=true)
94+
@test L * v == A * v == mul!(w, L, v)
95+
@test adjoint(L) * v A'v mul!(w, L', v)
96+
@test transpose(L) * v transpose(A)*v mul!(w, transpose(L), v)
97+
L = LinearMap{ComplexF64}((y, x) -> mul!(y, A, x), 10; issymmetric=true)
98+
@test L * v == A * v == mul!(w, L, v)
99+
@test adjoint(L) * v A'v mul!(w, L', v)
100+
@test transpose(L) * v transpose(A)*v mul!(w, transpose(L), v)
101+
102+
A = Hermitian(rand(ComplexF64, 10, 10))
103+
L = LinearMap{ComplexF64}(x -> A*x, 10; ishermitian=true)
104+
@test L * v == A * v == mul!(w, L, v)
105+
@test adjoint(L) * v A'v mul!(w, L', v)
106+
@test transpose(L) * v transpose(A)*v mul!(w, transpose(L), v)
107+
L = LinearMap{ComplexF64}((y, x) -> mul!(y, A, x), 10; ishermitian=true)
108+
@test L * v == A * v == mul!(w, L, v)
109+
@test adjoint(L) * v A'v mul!(w, L', v)
110+
@test transpose(L) * v transpose(A)*v mul!(w, transpose(L), v)
87111
end

0 commit comments

Comments
 (0)