Skip to content

Commit e05375f

Browse files
authored
Specialize Base.axes for [Composite/Adjoint/Transpose]Maps (#164)
1 parent e575b5f commit e05375f

File tree

6 files changed

+45
-11
lines changed

6 files changed

+45
-11
lines changed

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 = "3.5.0"
3+
version = "3.5.1"
44

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

docs/src/history.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
## What's new in v3.5
44

5-
* `WrappedMap`, `ScaledMap`, and `LinearCombination`, instead of using the default `axes(A)
6-
= map(oneto, size(A))`, now forward calls to `axes` to the underlying wrapped linear map.
7-
This allows allocating operations such as `*` to determine the appropriate storage and axes
8-
type of their outputs. For example, linear maps that wrap `BlockArrays` will, upon
9-
multiplicative action, produce a `BlockArrays.PseudoBlockVector` with block structure
10-
inherited from the operator's *output* axes `axes(A,1)`.
5+
* `WrappedMap`, `ScaledMap`, `LinearCombination`, `AdjointMap`, `TransposeMap` and
6+
`CompositeMap`, instead of using the default `axes(A) = map(oneto, size(A))`, now forward
7+
calls to `axes` to the underlying wrapped linear map. This allows allocating operations
8+
such as `*` to determine the appropriate storage and axes type of their outputs.
9+
For example, linear maps that wrap `BlockArrays` will, upon multiplicative action,
10+
produce a `BlockArrays.PseudoBlockVector` with block structure inherited from the
11+
operator's *output* axes `axes(A,1)`.
1112

1213
## What's new in v3.4
1314

src/LinearMaps.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ LinearAlgebra.isposdef(::LinearMap) = false # default assumptions
4949
Base.ndims(::LinearMap) = 2
5050
Base.size(A::LinearMap, n) =
5151
(n == 1 || n == 2 ? size(A)[n] : error("LinearMap objects have only 2 dimensions"))
52-
Base.length(A::LinearMap) = size(A)[1] * size(A)[2]
52+
Base.axes(A::LinearMap, n::Integer) =
53+
(n == 1 || n == 2 ? axes(A)[n] : error("LinearMap objects have only 2 dimensions"))
54+
Base.length(A::LinearMap) = prod(size(A))
5355

5456
# check dimension consistency for multiplication A*B
5557
_iscompatible((A, B)) = size(A, 2) == size(B, 1)

src/composition.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ CompositeMap{T}(maps::As) where {T, As<:LinearMapTuple} = CompositeMap{T, As}(ma
1717

1818
# basic methods
1919
Base.size(A::CompositeMap) = (size(A.maps[end], 1), size(A.maps[1], 2))
20+
Base.axes(A::CompositeMap) = (axes(A.maps[end])[1], axes(A.maps[1])[2])
2021
Base.isreal(A::CompositeMap) = all(isreal, A.maps) # sufficient but not necessary
2122

2223
# the following rules are sufficient but not necessary

src/transpose.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ LinearAlgebra.adjoint(A::LinearMap{<:Real}) = transpose(A)
3434

3535
# properties
3636
Base.size(A::Union{TransposeMap, AdjointMap}) = reverse(size(A.lmap))
37+
Base.axes(A::Union{TransposeMap, AdjointMap}) = reverse(axes(A.lmap))
3738
LinearAlgebra.issymmetric(A::Union{TransposeMap, AdjointMap}) = issymmetric(A.lmap)
3839
LinearAlgebra.ishermitian(A::Union{TransposeMap, AdjointMap}) = ishermitian(A.lmap)
3940
LinearAlgebra.isposdef(A::Union{TransposeMap, AdjointMap}) = isposdef(A.lmap)

test/nontradaxes.jl

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ using Test, LinearMaps, LinearAlgebra, BlockArrays
1212

1313
N = @inferred LinearMap(B)
1414
@test axes(N) == (ax1,ax2)
15+
@test axes(N, 1) == ax1
16+
@test axes(N, 2) == ax2
17+
@test_throws ErrorException axes(N, 3)
1518

1619
@test eltype(N) == eltype(B)
1720

1821
u = similar(Array{ComplexF64}, ax2)
1922
v = PseudoBlockVector{ComplexF64}(undef, [3,5])
20-
w = PseudoBlockVector{ComplexF64}(undef, [4,3])
21-
# v = similar(Array{ComplexF64}, blockedrange([3,5]))
22-
# w = similar(Array{ComplexF64}, blockedrange([4,3]))
23+
w = similar(Array{ComplexF64}, ax1)
2324

2425
for i in eachindex(u) u[i] = rand(ComplexF64) end
2526
for i in eachindex(v) v[i] = rand(ComplexF64) end
@@ -34,11 +35,39 @@ using Test, LinearMaps, LinearAlgebra, BlockArrays
3435
@test axes(Nu)[1] == axes(N)[1] == axes(B)[1]
3536
@test blocklengths(axes(Nu)[1]) == blocklengths(axes(N)[1]) == blocklengths(axes(B)[1]) == [2,3]
3637

38+
for trans in (adjoint, transpose)
39+
Nt = trans(LinearMap(N))
40+
Bt = trans(B)
41+
Ntw = Nt*w
42+
Btw = Bt*w
43+
@test Ntw Btw
44+
@test axes(Ntw)[1] == axes(Nt)[1] == axes(Bt)[1]
45+
@test blocklengths(axes(Ntw)[1]) == blocklengths(axes(Nt)[1]) == blocklengths(axes(Bt)[1]) == [3,4]
46+
end
47+
3748
C = B + 2N
3849
@test axes(C) === axes(B) === axes(N)
3950
@test C*u 3*Nu
4051

4152
Cu = C*u
4253
@test axes(C)[1] == ax1
4354
@test blocklengths(axes(C)[1]) == blocklengths(ax1)
55+
56+
A = rand(ComplexF64,2,2)
57+
B = PseudoBlockMatrix{ComplexF64}(undef, [2,2], [2,2])
58+
ax1 = axes(B)[1]
59+
ax2 = axes(B)[2]
60+
fill!(B,0)
61+
B[Block(1),Block(2)] .= A
62+
L = LinearMap(B)
63+
L2 = L*L
64+
B2 = B*B
65+
@test axes(L2) == axes(B2)
66+
B2 = B*Matrix(B)
67+
L2 = L*LinearMap(Matrix(B))
68+
@test axes(L2) == axes(B2)
69+
u = similar(Array{ComplexF64}, ax2)
70+
B2u = B2*u; L2u = L2*u
71+
@test axes(B2u)[1] == axes(L2u)[1] == axes(B2)[1] == axes(L2)[1]
72+
@test blocklengths(axes(B2u)[1]) == blocklengths(axes(L2u)[1]) == [2,2]
4473
end

0 commit comments

Comments
 (0)