Skip to content

Commit 3639b9f

Browse files
authored
Overhaul of conversion methods (#88)
1 parent 9639b0c commit 3639b9f

File tree

10 files changed

+201
-126
lines changed

10 files changed

+201
-126
lines changed

src/conversion.jl

Lines changed: 89 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ function Base.Matrix(A::LinearMap)
44
T = eltype(A)
55
mat = Matrix{T}(undef, (M, N))
66
v = fill(zero(T), N)
7-
@inbounds for i = 1:N
7+
@inbounds for i in 1:N
88
v[i] = one(T)
99
mul!(view(mat, :, i), A, v)
1010
v[i] = zero(T)
@@ -17,35 +17,6 @@ Base.convert(::Type{Array}, A::LinearMap) = convert(Matrix, A)
1717
Base.convert(::Type{AbstractMatrix}, A::LinearMap) = convert(Matrix, A)
1818
Base.convert(::Type{AbstractArray}, A::LinearMap) = convert(AbstractMatrix, A)
1919

20-
# special cases
21-
Base.convert(::Type{AbstractMatrix}, A::WrappedMap) = convert(AbstractMatrix, A.lmap)
22-
Base.convert(::Type{Matrix}, A::WrappedMap) = convert(Matrix, A.lmap)
23-
function Base.convert(::Type{Matrix}, ΣA::LinearCombination{<:Any,<:Tuple{Vararg{MatrixMap}}})
24-
if length(ΣA.maps) <= 10
25-
return (+).(map(A->getfield(A, :lmap), ΣA.maps)...)
26-
else
27-
S = zero(first(ΣA.maps).lmap)
28-
for A in ΣA.maps
29-
S .+= A.lmap
30-
end
31-
return S
32-
end
33-
end
34-
function Base.convert(::Type{Matrix}, AB::CompositeMap{<:Any,<:Tuple{MatrixMap,MatrixMap}})
35-
B, A = AB.maps
36-
return A.lmap*B.lmap
37-
end
38-
function Base.convert(::Type{Matrix}, λA::CompositeMap{<:Any,<:Tuple{MatrixMap,UniformScalingMap}})
39-
A, J = λA.maps
40-
return J.λ*A.lmap
41-
end
42-
function Base.convert(::Type{Matrix}, Aλ::CompositeMap{<:Any,<:Tuple{UniformScalingMap,MatrixMap}})
43-
J, A =.maps
44-
return A.lmap*J.λ
45-
end
46-
47-
Base.Matrix(A::BlockMap) = hvcat(A.rows, convert.(AbstractMatrix, A.maps)...)
48-
4920
# sparse: create sparse matrix representation of LinearMap
5021
function SparseArrays.sparse(A::LinearMap{T}) where {T}
5122
M, N = size(A)
@@ -55,7 +26,7 @@ function SparseArrays.sparse(A::LinearMap{T}) where {T}
5526
v = fill(zero(T), N)
5627
Av = Vector{T}(undef, M)
5728

58-
for i = 1:N
29+
@inbounds for i in 1:N
5930
v[i] = one(T)
6031
mul!(Av, A, v)
6132
js = findall(!iszero, Av)
@@ -70,7 +41,92 @@ function SparseArrays.sparse(A::LinearMap{T}) where {T}
7041

7142
return SparseMatrixCSC(M, N, colptr, rowind, nzval)
7243
end
73-
7444
Base.convert(::Type{SparseMatrixCSC}, A::LinearMap) = sparse(A)
45+
46+
# special cases
47+
48+
# UniformScalingMap
49+
Base.Matrix(J::UniformScalingMap) = Matrix(J.λ*I, size(J))
50+
Base.convert(::Type{AbstractMatrix}, J::UniformScalingMap) = Diagonal(fill(J.λ, size(J, 1)))
51+
52+
# WrappedMap
53+
Base.Matrix(A::WrappedMap) = Matrix(A.lmap)
54+
Base.convert(::Type{AbstractMatrix}, A::WrappedMap) = convert(AbstractMatrix, A.lmap)
55+
Base.convert(::Type{Matrix}, A::WrappedMap) = convert(Matrix, A.lmap)
56+
SparseArrays.sparse(A::WrappedMap) = sparse(A.lmap)
7557
Base.convert(::Type{SparseMatrixCSC}, A::WrappedMap) = convert(SparseMatrixCSC, A.lmap)
76-
Base.convert(::Type{SparseMatrixCSC}, A::BlockMap) = hvcat(A.rows, convert.(SparseMatrixCSC, A.maps)...)
58+
59+
# TransposeMap & AdjointMap
60+
for (TT, T) in ((AdjointMap, adjoint), (TransposeMap, transpose))
61+
@eval Base.convert(::Type{AbstractMatrix}, A::$TT) = $T(convert(AbstractMatrix, A.lmap))
62+
@eval SparseArrays.sparse(A::$TT) = $T(convert(SparseMatrixCSC, A.lmap))
63+
end
64+
65+
# LinearCombination
66+
for (TT, T) in ((Type{Matrix}, Matrix), (Type{SparseMatrixCSC}, SparseMatrixCSC))
67+
@eval function Base.convert(::$TT, ΣA::LinearCombination{<:Any,<:Tuple{Vararg{MatrixMap}}})
68+
maps = ΣA.maps
69+
mats = map(A->getfield(A, :lmap), maps)
70+
return convert($T, sum(mats))
71+
end
72+
end
73+
74+
# CompositeMap
75+
for (TT, T) in ((Type{Matrix}, Matrix), (Type{SparseMatrixCSC}, SparseMatrixCSC))
76+
@eval function Base.convert(::$TT, AB::CompositeMap{<:Any,<:Tuple{MatrixMap,MatrixMap}})
77+
B, A = AB.maps
78+
return convert($T, A.lmap*B.lmap)
79+
end
80+
@eval function Base.convert(::$TT, λA::CompositeMap{<:Any,<:Tuple{MatrixMap,UniformScalingMap}})
81+
A, J = λA.maps
82+
return convert($T, J.λ*A.lmap)
83+
end
84+
@eval function Base.convert(::$TT, Aλ::CompositeMap{<:Any,<:Tuple{UniformScalingMap,MatrixMap}})
85+
J, A =.maps
86+
return convert($T, A.lmap*J.λ)
87+
end
88+
end
89+
90+
# BlockMap & BlockDiagonalMap
91+
Base.Matrix(A::BlockMap) = hvcat(A.rows, convert.(Matrix, A.maps)...)
92+
Base.convert(::Type{AbstractMatrix}, A::BlockMap) = hvcat(A.rows, convert.(AbstractMatrix, A.maps)...)
93+
function Base.convert(::Type{SparseMatrixCSC}, A::BlockMap)
94+
return hvcat(
95+
A.rows,
96+
convert(SparseMatrixCSC, first(A.maps)),
97+
convert.(AbstractMatrix, Base.tail(A.maps))...
98+
)
99+
end
100+
Base.Matrix(A::BlockDiagonalMap) = cat(convert.(Matrix, A.maps)...; dims=(1,2))
101+
Base.convert(::Type{AbstractMatrix}, A::BlockDiagonalMap) = sparse(A)
102+
function SparseArrays.sparse(A::BlockDiagonalMap)
103+
return blockdiag(convert.(SparseMatrixCSC, A.maps)...)
104+
end
105+
106+
# KroneckerMap & KroneckerSumMap
107+
Base.Matrix(A::KroneckerMap) = kron(convert.(Matrix, A.maps)...)
108+
Base.convert(::Type{AbstractMatrix}, A::KroneckerMap) = kron(convert.(AbstractMatrix, A.maps)...)
109+
function SparseArrays.sparse(A::KroneckerMap)
110+
return kron(
111+
convert(SparseMatrixCSC, first(A.maps)),
112+
convert.(AbstractMatrix, Base.tail(A.maps))...
113+
)
114+
end
115+
function Base.Matrix(L::KroneckerSumMap)
116+
A, B = L.maps
117+
IA = Diagonal(ones(Bool, size(A, 1)))
118+
IB = Diagonal(ones(Bool, size(B, 1)))
119+
return kron(Matrix(A), IB) + kron(IA, Matrix(B))
120+
end
121+
function Base.convert(::Type{AbstractMatrix}, L::KroneckerSumMap)
122+
A, B = L.maps
123+
IA = Diagonal(ones(Bool, size(A, 1)))
124+
IB = Diagonal(ones(Bool, size(B, 1)))
125+
return kron(convert(AbstractMatrix, A), IB) + kron(IA, convert(AbstractMatrix, B))
126+
end
127+
function SparseArrays.sparse(L::KroneckerSumMap)
128+
A, B = L.maps
129+
IA = sparse(Diagonal(ones(Bool, size(A, 1))))
130+
IB = sparse(Diagonal(ones(Bool, size(B, 1))))
131+
return kron(convert(AbstractMatrix, A), IB) + kron(IA, convert(AbstractMatrix, B))
132+
end

src/uniformscalingmap.jl

Lines changed: 23 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -33,85 +33,57 @@ Base.:(*)(J::UniformScalingMap, α::Number) = UniformScalingMap(J.λ * α, size(
3333

3434
# multiplication with vector
3535
Base.:(*)(A::UniformScalingMap, x::AbstractVector) =
36-
length(x) == A.M ? A.λ * x : throw(DimensionMismatch("A_mul_B!"))
36+
length(x) == A.M ? A.λ * x : throw(DimensionMismatch("*"))
3737

38-
if VERSION < v"1.3.0-alpha.115"
39-
function A_mul_B!(y::AbstractVector, A::UniformScalingMap, x::AbstractVector)
40-
(length(x) == length(y) == A.M || throw(DimensionMismatch("A_mul_B!")))
41-
if iszero(A.λ)
42-
return fill!(y, zero(eltype(y)))
43-
elseif isone(A.λ)
44-
return copyto!(y, x)
45-
else
46-
# call of LinearAlgebra.generic_mul! since order of arguments in mul! in
47-
# stdlib/LinearAlgebra/src/generic.jl reversed
48-
return LinearAlgebra.generic_mul!(y, A.λ, x)
38+
# multiplication with vector/matrix
39+
for Atype in (AbstractVector, AbstractMatrix)
40+
@eval Base.@propagate_inbounds function LinearAlgebra.mul!(y::$Atype, J::UniformScalingMap, x::$Atype,
41+
α::Number=true, β::Number=false)
42+
@boundscheck check_dim_mul(y, J, x)
43+
_scaling!(y, J.λ, x, α, β)
44+
return y
4945
end
5046
end
51-
else # 5-arg mul! exists and order of arguments is corrected
52-
53-
A_mul_B!(y::AbstractVector, J::UniformScalingMap, x::AbstractVector) = mul!(y, J, x)
54-
55-
end # VERSION
56-
57-
@inline function LinearAlgebra.mul!(y::AbstractVector, J::UniformScalingMap, x::AbstractVector, α::Number=true, β::Number=false)
58-
@boundscheck (length(x) == length(y) == J.M || throw(DimensionMismatch("mul!")))
59-
_scaling!(y, J, x, α, β)
60-
return y
61-
end
6247

63-
@inline function LinearAlgebra.mul!(Y::AbstractMatrix, J::UniformScalingMap, X::AbstractMatrix, α::Number=true, β::Number=false)
64-
@boundscheck size(X) == size(Y) || throw(DimensionMismatch("mul!"))
65-
@boundscheck size(X,1) == J.M || throw(DimensionMismatch("mul!"))
66-
_scaling!(Y, J, X, α, β)
67-
return Y
68-
end
69-
70-
function _scaling!(y, J::UniformScalingMap, x, α::Number=true, β::Number=false)
71-
λ = J.λ
72-
if isone(α)
48+
function _scaling!(y, λ::Number, x, α::Number, β::Number)
49+
if (iszero(α) || iszero(λ))
50+
iszero(β) && (fill!(y, zero(eltype(y))); return y)
51+
isone(β) && return y
52+
# β != 0, 1
53+
rmul!(y, β)
54+
return y
55+
elseif isone(α)
7356
if iszero(β)
74-
iszero(λ) && return fill!(y, zero(eltype(y)))
7557
isone(λ) && return copyto!(y, x)
7658
y .= λ .* x
7759
return y
7860
elseif isone(β)
79-
iszero(λ) && return y
8061
isone(λ) && return y .+= x
8162
y .+= λ .* x
8263
return y
8364
else # β != 0, 1
84-
iszero(λ) && (rmul!(y, β); return y)
85-
isone(λ) && (y .= y .* β .+ x; return y)
65+
isone(λ) && (axpby!(one(eltype(x)), x, β, y); return y)
8666
y .= y .* β .+ λ .* x
8767
return y
8868
end
89-
elseif iszero(α)
90-
iszero(β) && (fill!(y, zero(eltype(y))); return y)
91-
isone(β) && return y
92-
# β != 0, 1
93-
rmul!(y, β)
94-
return y
9569
else # α != 0, 1
9670
if iszero(β)
97-
iszero(λ) && return fill!(y, zero(eltype(y)))
98-
isone(λ) && return y .= x .* α
71+
isone(λ) && (y .= x .* α; return y)
9972
y .= λ .* x .* α
10073
return y
10174
elseif isone(β)
102-
iszero(λ) && return y
103-
isone(λ) && return y .+= x .* α
75+
isone(λ) && (axpby!(α, x, β, y); return y)
10476
y .+= λ .* x .* α
10577
return y
10678
else # β != 0, 1
107-
iszero(λ) && (rmul!(y, β); return y)
10879
isone(λ) && (y .= y .* β .+ x .* α; return y)
10980
y .= y .* β .+ λ .* x .* α
11081
return y
111-
end
112-
end
113-
end
82+
end # β-cases
83+
end # α-cases
84+
end # function _scaling!
11485

86+
A_mul_B!(y::AbstractVector, A::UniformScalingMap, x::AbstractVector) = mul!(y, A, x)
11587
At_mul_B!(y::AbstractVector, A::UniformScalingMap, x::AbstractVector) = A_mul_B!(y, transpose(A), x)
11688
Ac_mul_B!(y::AbstractVector, A::UniformScalingMap, x::AbstractVector) = A_mul_B!(y, adjoint(A), x)
11789

test/blockmap.jl

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays
7171
@test L isa LinearMaps.BlockMap{elty}
7272
@test size(L) == size(A)
7373
@test L * x A * x
74-
@test Matrix(L) A
74+
@test Matrix(L) == A
75+
@test convert(AbstractMatrix, L) == A
7576
A = [I A12; A21 I]
7677
@inferred hvcat((2,2), I, LinearMap(A12), LinearMap(A21), I)
7778
L = @inferred hvcat((2,2), I, LinearMap(A12), LinearMap(A21), I)
@@ -136,13 +137,17 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays
136137
@test L isa LinearMaps.LinearMap{elty}
137138
@test size(L) == size(A)
138139
@test L * x A * x
139-
@test Matrix(L) A
140+
@test Matrix(L) == A
141+
@test convert(AbstractMatrix, L) == A
142+
@test sparse(L) == sparse(A)
140143
Lt = @inferred transform(L)
141144
@test Lt isa LinearMaps.LinearMap{elty}
142145
@test Lt * x transform(A) * x
146+
@test convert(AbstractMatrix, Lt) == transform(A)
147+
@test sparse(transform(L)) == transform(A)
143148
Lt = @inferred transform(LinearMap(L))
144149
@test Lt * x transform(A) * x
145-
@test Matrix(Lt) Matrix(transform(A))
150+
@test Matrix(Lt) == Matrix(transform(A))
146151
A21 = rand(elty, 10, 10)
147152
A = [I A12; A21 I]
148153
L = [I LinearMap(A12); LinearMap(A21) I]
@@ -170,6 +175,9 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays
170175
Md = Matrix(blockdiag(sparse.((M1, M2, M3, M2, M1))...))
171176
x = randn(elty, size(Md, 2))
172177
Bd = @inferred blockdiag(L1, L2, L3, L2, L1)
178+
@test Matrix(Bd) == Md
179+
@test convert(AbstractMatrix, Bd) isa SparseMatrixCSC
180+
@test sparse(Bd) == Md
173181
@test Matrix(@inferred blockdiag(L1)) == M1
174182
@test Matrix(@inferred blockdiag(L1, L2)) == blockdiag(sparse.((M1, M2))...)
175183
Bd2 = @inferred cat(L1, L2, L3, L2, L1; dims=(1,2))

test/composition.jl

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,5 @@
11
using Test, LinearMaps, LinearAlgebra
22

3-
# new type
4-
struct SimpleFunctionMap <: LinearMap{Float64}
5-
f::Function
6-
N::Int
7-
end
8-
struct SimpleComplexFunctionMap <: LinearMap{Complex{Float64}}
9-
f::Function
10-
N::Int
11-
end
12-
Base.size(A::Union{SimpleFunctionMap,SimpleComplexFunctionMap}) = (A.N, A.N)
13-
Base.:(*)(A::Union{SimpleFunctionMap,SimpleComplexFunctionMap}, v::Vector) = A.f(v)
14-
LinearAlgebra.mul!(y::Vector, A::Union{SimpleFunctionMap,SimpleComplexFunctionMap}, x::Vector) = copyto!(y, *(A, x))
15-
163
@testset "composition" begin
174
F = @inferred LinearMap(cumsum, y -> reverse(cumsum(reverse(x))), 10; ismutating=false)
185
FC = @inferred LinearMap{ComplexF64}(cumsum, y -> reverse(cumsum(reverse(x))), 10; ismutating=false)
@@ -66,24 +53,6 @@ LinearAlgebra.mul!(y::Vector, A::Union{SimpleFunctionMap,SimpleComplexFunctionMa
6653
mul!(w, L, v)
6754
@test w LF * v
6855

69-
# test new type
70-
F = SimpleFunctionMap(cumsum, 10)
71-
FC = SimpleComplexFunctionMap(cumsum, 10)
72-
@test @inferred ndims(F) == 2
73-
@test @inferred size(F, 1) == 10
74-
@test @inferred length(F) == 100
75-
@test @inferred !issymmetric(F)
76-
@test @inferred !ishermitian(F)
77-
@test @inferred !ishermitian(FC)
78-
@test @inferred !isposdef(F)
79-
w = similar(v)
80-
mul!(w, F, v)
81-
@test w == F * v
82-
@test_throws MethodError F' * v
83-
@test_throws MethodError transpose(F) * v
84-
@test_throws MethodError mul!(w, adjoint(F), v)
85-
@test_throws MethodError mul!(w, transpose(F), v)
86-
8756
# test composition of several maps with shared data #31
8857
global sizes = ( (5, 2), (3, 3), (3, 2), (2, 2), (9, 2), (7, 1) )
8958
N = length(sizes) - 1

test/conversion.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays
2424
@test Matrix(transpose(M)) == copy(transpose(A))
2525
@test convert(AbstractMatrix, M') isa Adjoint
2626
@test convert(Matrix, M*3I) == A*3
27+
@test convert(Matrix, M+M) == A + A
28+
29+
# UniformScalingMap
30+
J = LinearMap*I, 10)
31+
JM = convert(AbstractMatrix, J)
32+
@test JM == Diagonal(fill(α, 10))
2733

2834
# sparse matrix generation/conversion
2935
@test sparse(M) == sparse(Array(M))

0 commit comments

Comments
 (0)