Skip to content

Commit 98cc86a

Browse files
authored
Use 5-arg mul! for matrices (#68)
* intro MatrixMap, 5-arg mul! for WrappedMaps -> LinAlg * make adjoint/transpose invariant for MatrixMaps * inherit properties of MatrixMap, add comparison * test TransposeMap/AdjointMap based on FunctionMaps * add specialized matrix-muls for linearcombinations * introduce FreeMap, test for zero allocations
1 parent 3d2e53f commit 98cc86a

File tree

5 files changed

+122
-8
lines changed

5 files changed

+122
-8
lines changed

src/LinearMaps.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,11 @@ function SparseArrays.sparse(A::LinearMap{T}) where {T}
105105
return SparseMatrixCSC(M, N, colptr, rowind, nzval)
106106
end
107107

108+
include("wrappedmap.jl") # wrap a matrix of linear map in a new type, thereby allowing to alter its properties
109+
include("uniformscalingmap.jl") # the uniform scaling map, to be able to make linear combinations of LinearMap objects and multiples of I
108110
include("transpose.jl") # transposing linear maps
109111
include("linearcombination.jl") # defining linear combinations of linear maps
110112
include("composition.jl") # composition of linear maps
111-
include("wrappedmap.jl") # wrap a matrix of linear map in a new type, thereby allowing to alter its properties
112-
include("uniformscalingmap.jl") # the uniform scaling map, to be able to make linear combinations of LinearMap objects and multiples of I
113113
include("functionmap.jl") # using a function as linear map
114114
include("blockmap.jl") # block linear maps
115115

src/linearcombination.jl

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ LinearAlgebra.transpose(A::LinearCombination) = LinearCombination{eltype(A)}(map
4646
LinearAlgebra.adjoint(A::LinearCombination) = LinearCombination{eltype(A)}(map(adjoint, A.maps))
4747

4848
# multiplication with vectors
49+
if VERSION < v"1.3.0-alpha.115"
50+
4951
function A_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector)
5052
# no size checking, will be done by individual maps
5153
A_mul_B!(y, A.maps[1], x)
@@ -60,6 +62,67 @@ function A_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector)
6062
return y
6163
end
6264

65+
else # 5-arg mul! is available for matrices
66+
67+
# map types that have an allocation-free 5-arg mul! implementation
68+
const FreeMap = Union{MatrixMap,UniformScalingMap}
69+
70+
function A_mul_B!(y::AbstractVector, A::LinearCombination{T,As}, x::AbstractVector) where {T, As<:Tuple{Vararg{FreeMap}}}
71+
# no size checking, will be done by individual maps
72+
A_mul_B!(y, A.maps[1], x)
73+
for n in 2:length(A.maps)
74+
mul!(y, A.maps[n], x, true, true)
75+
end
76+
return y
77+
end
78+
function A_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector)
79+
# no size checking, will be done by individual maps
80+
A_mul_B!(y, A.maps[1], x)
81+
l = length(A.maps)
82+
if l>1
83+
z = similar(y)
84+
for n in 2:l
85+
An = A.maps[n]
86+
if An isa FreeMap
87+
mul!(y, An, x, true, true)
88+
else
89+
A_mul_B!(z, A.maps[n], x)
90+
y .+= z
91+
end
92+
end
93+
end
94+
return y
95+
end
96+
97+
function LinearAlgebra.mul!(y::AbstractVector, A::LinearCombination{T,As}, x::AbstractVector, α::Number=true, β::Number=false) where {T, As<:Tuple{Vararg{FreeMap}}}
98+
length(y) == size(A, 1) || throw(DimensionMismatch("mul!"))
99+
if isone(α)
100+
iszero(β) && (A_mul_B!(y, A, x); return y)
101+
!isone(β) && rmul!(y, β)
102+
elseif iszero(α)
103+
iszero(β) && (fill!(y, zero(eltype(y))); return y)
104+
isone(β) && return y
105+
# β != 0, 1
106+
rmul!(y, β)
107+
return y
108+
else # α != 0, 1
109+
if iszero(β)
110+
A_mul_B!(y, A, x)
111+
rmul!(y, α)
112+
return y
113+
elseif !isone(β)
114+
rmul!(y, β)
115+
end # β-cases
116+
end # α-cases
117+
118+
for An in A.maps
119+
mul!(y, An, x, α, true)
120+
end
121+
return y
122+
end
123+
124+
end # VERSION
125+
63126
At_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector) = A_mul_B!(y, transpose(A), x)
64127

65128
Ac_mul_B!(y::AbstractVector, A::LinearCombination, x::AbstractVector) = A_mul_B!(y, adjoint(A), x)

src/wrappedmap.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@ function WrappedMap{T}(lmap::Union{AbstractMatrix, LinearMap};
1717
WrappedMap{T, typeof(lmap)}(lmap, issymmetric, ishermitian, isposdef)
1818
end
1919

20+
const MatrixMap{T} = WrappedMap{T,<:AbstractMatrix}
21+
22+
LinearAlgebra.transpose(A::MatrixMap{T}) where {T} =
23+
WrappedMap{T}(transpose(A.lmap); issymmetric=A._issymmetric, ishermitian=A._ishermitian, isposdef=A._isposdef)
24+
LinearAlgebra.adjoint(A::MatrixMap{T}) where {T} =
25+
WrappedMap{T}(adjoint(A.lmap); issymmetric=A._issymmetric, ishermitian=A._ishermitian, isposdef=A._isposdef)
26+
27+
Base.:(==)(A::MatrixMap, B::MatrixMap) =
28+
(eltype(A)==eltype(B) && A.lmap==B.lmap && A._issymmetric==B._issymmetric &&
29+
A._ishermitian==B._ishermitian && A._isposdef==B._isposdef)
30+
31+
if VERSION v"1.3.0-alpha.115"
32+
33+
LinearAlgebra.mul!(y::AbstractVector, A::WrappedMap, x::AbstractVector, α::Number=true, β::Number=false) =
34+
mul!(y, A.lmap, x, α, β)
35+
36+
end # VERSION
37+
2038
# properties
2139
Base.size(A::WrappedMap) = size(A.lmap)
2240
LinearAlgebra.issymmetric(A::WrappedMap) = A._issymmetric

test/linearcombination.jl

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,27 @@ using Test, LinearMaps, LinearAlgebra, BenchmarkTools
1515
@test run(b, samples=5).allocs <= 1
1616

1717
A = 2 * rand(ComplexF64, (10, 10)) .- 1
18-
B = rand(size(A)...)
18+
B = rand(ComplexF64, size(A)...)
1919
M = @inferred LinearMap(A)
2020
N = @inferred LinearMap(B)
2121
LC = @inferred M + N
2222
v = rand(ComplexF64, 10)
2323
w = similar(v)
2424
b = @benchmarkable mul!($w, $M, $v)
2525
@test run(b, samples=3).allocs == 0
26+
if VERSION >= v"1.3.0-alpha.115"
27+
b = @benchmarkable mul!($w, $LC, $v)
28+
@test run(b, samples=3).allocs == 0
29+
for α in (false, true, rand(ComplexF64)), β in (false, true, rand(ComplexF64))
30+
b = @benchmarkable mul!($w, $LC, $v, $α, $β)
31+
@test run(b, samples=3).allocs == 0
32+
b = @benchmarkable mul!($w, $(LC + I), $v, $α, $β)
33+
@test run(b, samples=3).allocs == 0
34+
y = rand(ComplexF64, size(v))
35+
@test mul!(copy(y), LC, v, α, β) Matrix(LC)*v*α + y*β
36+
@test mul!(copy(y), LC+I, v, α, β) Matrix(LC + I)*v*α + y*β
37+
end
38+
end
2639
# @test_throws ErrorException LinearMaps.LinearCombination{ComplexF64}((M, N), (1, 2, 3))
2740
@test @inferred size(3M + 2.0N) == size(A)
2841
# addition
@@ -31,16 +44,16 @@ using Test, LinearMaps, LinearAlgebra, BenchmarkTools
3144
@test @inferred convert(Matrix, M + LC) 2A + B
3245
@test @inferred convert(Matrix, M + M) 2A
3346
# subtraction
34-
@test @inferred Matrix(LC - LC) == zeros(size(LC))
35-
@test @inferred Matrix(LC - M) == B
36-
@test @inferred Matrix(N - LC) == -A
37-
@test @inferred Matrix(M - M) == zeros(size(M))
47+
@test (@inferred Matrix(LC - LC)) zeros(eltype(LC), size(LC)) atol=10eps()
48+
@test (@inferred Matrix(LC - M)) B
49+
@test (@inferred Matrix(N - LC)) -A
50+
@test (@inferred Matrix(M - M)) zeros(size(M)) atol=10eps()
3851
# scalar multiplication
3952
@test @inferred Matrix(-M) == -A
4053
@test @inferred Matrix(-LC) == -A - B
4154
@test @inferred Matrix(3 * M) == 3 * A
4255
@test @inferred Matrix(M * 3) == 3 * A
43-
@test Matrix(3.0 * LC) Matrix(LC * 3) == 3A + 3B
56+
@test Matrix(3.0 * LC) Matrix(LC * 3) 3A + 3B
4457
@test @inferred Matrix(3 \ M) A/3
4558
@test @inferred Matrix(M / 3) A/3
4659
@test @inferred Matrix(3 \ LC) (A + B) / 3

test/transpose.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,24 @@ using Test, LinearMaps, LinearAlgebra
4949
B = @inferred LinearMap(Hermitian(rand(ComplexF64, 10, 10)))
5050
@test adjoint(B) == B
5151
@test B == B'
52+
53+
CS = @inferred LinearMap{ComplexF64}(cumsum, x -> reverse(cumsum(reverse(x))), 10; ismutating=false)
54+
for transform in (adjoint, transpose)
55+
@test transform(transform(CS)) == CS
56+
end
57+
@test transpose(CS) != adjoint(CS)
58+
@test adjoint(CS) != transpose(CS)
59+
M = Matrix(CS)
60+
x = rand(ComplexF64, 10)
61+
for transform1 in (adjoint, transpose), transform2 in (adjoint, transpose)
62+
@test transform2(LinearMap(transform1(CS))) * x transform2(transform1(M))*x
63+
end
64+
65+
id = @inferred LinearMap(identity, identity, 10; issymmetric=true, ishermitian=true, isposdef=true)
66+
for transform in (adjoint, transpose)
67+
@test transform(id) == id
68+
for prop in (issymmetric, ishermitian, isposdef)
69+
@test prop(transform(id))
70+
end
71+
end
5272
end

0 commit comments

Comments
 (0)