Skip to content

Commit 630d0a0

Browse files
authored
Introduce specific show methods, add Base.parent overload (#87)
1 parent 2d8dcdf commit 630d0a0

22 files changed

+231
-101
lines changed

docs/src/types.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ Abstract supertype
1414
LinearMaps.LinearMap
1515
```
1616

17+
Unwrapping function
18+
19+
```@docs
20+
Base.parent
21+
```
22+
1723
### `FunctionMap`
1824

1925
Type for wrapping an arbitrary function that is supposed to implement the

src/LinearMaps.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ Base.ndims(::LinearMap) = 2
4848
Base.size(A::LinearMap, n) = (n==1 || n==2 ? size(A)[n] : error("LinearMap objects have only 2 dimensions"))
4949
Base.length(A::LinearMap) = size(A)[1] * size(A)[2]
5050

51+
"""
52+
parent(A::LinearMap)
53+
54+
Return the underlying "parent map". This parent map is what was passed as an argument to
55+
the specific `LinearMap` constructor, including implicit constructors and up to implicit
56+
promotion to a `LinearMap` subtype. The fallback is to return the input itself.
57+
"""
58+
Base.parent(A::LinearMap) = A
59+
5160
# check dimension consistency for multiplication A*B
5261
_iscompatible((A, B)) = size(A, 2) == size(B, 1)
5362
function check_dim_mul(A, B)
@@ -235,6 +244,7 @@ include("functionmap.jl") # using a function as linear map
235244
include("blockmap.jl") # block linear maps
236245
include("kronecker.jl") # Kronecker product of linear maps
237246
include("conversion.jl") # conversion of linear maps to matrices
247+
include("show.jl") # show methods for LinearMap objects
238248

239249
"""
240250
LinearMap(A::LinearMap; kwargs...)::WrappedMap

src/blockmap.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ BlockMap{T}(maps::As, rows::S) where {T,As<:Tuple{Vararg{LinearMap}},S} = BlockM
1717

1818
MulStyle(A::BlockMap) = MulStyle(A.maps...)
1919

20+
Base.parent(A::BlockMap) = A.maps
21+
2022
"""
2123
rowcolranges(maps, rows)
2224
@@ -458,6 +460,10 @@ Base.cat
458460

459461
Base.size(A::BlockDiagonalMap) = (last(A.rowranges[end]), last(A.colranges[end]))
460462

463+
MulStyle(A::BlockDiagonalMap) = MulStyle(A.maps...)
464+
465+
Base.parent(A::BlockDiagonalMap) = A.maps
466+
461467
LinearAlgebra.issymmetric(A::BlockDiagonalMap) = all(issymmetric, A.maps)
462468
LinearAlgebra.ishermitian(A::BlockDiagonalMap{<:Real}) = all(issymmetric, A.maps)
463469
LinearAlgebra.ishermitian(A::BlockDiagonalMap) = all(ishermitian, A.maps)

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<:Tuple{Vararg{LinearMap}}} = CompositeMap
1717
# basic methods
1818
Base.size(A::CompositeMap) = (size(A.maps[end], 1), size(A.maps[1], 2))
1919
Base.isreal(A::CompositeMap) = all(isreal, A.maps) # sufficient but not necessary
20+
Base.parent(A::CompositeMap) = A.maps
2021

2122
# the following rules are sufficient but not necessary
2223
for (f, _f, g) in ((:issymmetric, :_issymmetric, :transpose),

src/functionmap.jl

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,9 @@ FunctionMap{T}(f, M::Int; kwargs...) where {T} = FunctionMap{T}(f, nothi
2121
FunctionMap{T}(f, M::Int, N::Int; kwargs...) where {T} = FunctionMap{T}(f, nothing, M, N; kwargs...)
2222
FunctionMap{T}(f, fc, M::Int; kwargs...) where {T} = FunctionMap{T}(f, fc, M, M; kwargs...)
2323

24-
# show
25-
function Base.show(io::IO, A::FunctionMap{T, F, Nothing}) where {T, F}
26-
print(io, "LinearMaps.FunctionMap{$T}($(A.f), $(A.M), $(A.N); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))")
27-
end
28-
function Base.show(io::IO, A::FunctionMap{T}) where {T}
29-
print(io, "LinearMaps.FunctionMap{$T}($(A.f), $(A.fc), $(A.M), $(A.N); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))")
30-
end
31-
3224
# properties
3325
Base.size(A::FunctionMap) = (A.M, A.N)
26+
Base.parent(A::FunctionMap) = (A.f, A.fc)
3427
LinearAlgebra.issymmetric(A::FunctionMap) = A._issymmetric
3528
LinearAlgebra.ishermitian(A::FunctionMap) = A._ishermitian
3629
LinearAlgebra.isposdef(A::FunctionMap) = A._isposdef

src/kronecker.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ Base.:(^)(A::MapOrMatrix, ::KronPower{p}) where {p} =
8888

8989
Base.size(A::KroneckerMap) = map(*, size.(A.maps)...)
9090

91+
Base.parent(A::KroneckerMap) = A.maps
92+
9193
LinearAlgebra.issymmetric(A::KroneckerMap) = all(issymmetric, A.maps)
9294
LinearAlgebra.ishermitian(A::KroneckerMap{<:Real}) = issymmetric(A)
9395
LinearAlgebra.ishermitian(A::KroneckerMap) = all(ishermitian, A.maps)
@@ -123,7 +125,7 @@ end
123125
if nb*ma < mb*na
124126
_unsafe_mul!(Y, B, Matrix(X*At))
125127
else
126-
_unsafe_mul!(Y, Matrix(B*X), At isa MatrixMap ? At.lmap : At.λ)
128+
_unsafe_mul!(Y, Matrix(B*X), parent(At))
127129
end
128130
return y
129131
end
@@ -248,6 +250,7 @@ Base.:(^)(A::MapOrMatrix, ::KronSumPower{p}) where {p} = kronsum(ntuple(n -> con
248250

249251
Base.size(A::KroneckerSumMap, i) = prod(size.(A.maps, i))
250252
Base.size(A::KroneckerSumMap) = (size(A, 1), size(A, 2))
253+
Base.parent(A::KroneckerSumMap) = A.maps
251254

252255
LinearAlgebra.issymmetric(A::KroneckerSumMap) = all(issymmetric, A.maps)
253256
LinearAlgebra.ishermitian(A::KroneckerSumMap{<:Real}) = all(issymmetric, A.maps)

src/linearcombination.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ MulStyle(A::LinearCombination) = MulStyle(A.maps...)
1818

1919
# basic methods
2020
Base.size(A::LinearCombination) = size(A.maps[1])
21+
Base.parent(A::LinearCombination) = A.maps
2122
LinearAlgebra.issymmetric(A::LinearCombination) = all(issymmetric, A.maps) # sufficient but not necessary
2223
LinearAlgebra.ishermitian(A::LinearCombination) = all(ishermitian, A.maps) # sufficient but not necessary
2324
LinearAlgebra.isposdef(A::LinearCombination) = all(isposdef, A.maps) # sufficient but not necessary

src/scaledmap.jl

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,9 @@ end
1111
ScaledMap::S, lmap::A) where {S<:RealOrComplex,A<:LinearMap} =
1212
ScaledMap{Base.promote_op(*, S, eltype(lmap))}(λ, lmap)
1313

14-
# show
15-
function Base.show(io::IO, A::ScaledMap{T}) where {T}
16-
println(io, "LinearMaps.ScaledMap{$T}, scale = $(A.λ)")
17-
show(io, A.lmap)
18-
end
19-
2014
# basic methods
2115
Base.size(A::ScaledMap) = size(A.lmap)
16+
Base.parent(A::ScaledMap) = (A.λ, A.lmap)
2217
Base.isreal(A::ScaledMap) = isreal(A.λ) && isreal(A.lmap)
2318
LinearAlgebra.issymmetric(A::ScaledMap) = issymmetric(A.lmap)
2419
LinearAlgebra.ishermitian(A::ScaledMap) = ishermitian(A.lmap)

src/show.jl

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# summary
2+
function Base.summary(io::IO, A::LinearMap)
3+
print(io, Base.dims2string(size(A)))
4+
print(io, ' ')
5+
_show_typeof(io, A)
6+
end
7+
8+
# show
9+
Base.show(io::IO, A::LinearMap) = (summary(io, A); _show(io, A))
10+
_show(io::IO, ::LinearMap) = nothing
11+
function _show(io::IO, A::FunctionMap{T,F,Nothing}) where {T,F}
12+
print(io, "($(A.f); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))")
13+
end
14+
function _show(io::IO, A::FunctionMap)
15+
print(io, "($(A.f), $(A.fc); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))")
16+
end
17+
function _show(io::IO, A::Union{CompositeMap,LinearCombination,KroneckerMap,KroneckerSumMap})
18+
n = length(A.maps)
19+
println(io, " with $n map", n>1 ? "s" : "", ":")
20+
print_maps(io, A.maps)
21+
end
22+
function _show(io::IO, A::Union{AdjointMap,TransposeMap,WrappedMap})
23+
print(io, " of ")
24+
L = A.lmap
25+
if A isa MatrixMap
26+
# summary(io, L)
27+
# println(io, ":")
28+
# Base.print_matrix(io, L)
29+
print(io, typeof(L))
30+
else
31+
show(io, L)
32+
end
33+
end
34+
function _show(io::IO, A::BlockMap)
35+
nrows = length(A.rows)
36+
n = length(A.maps)
37+
println(io, " with $n block map", n>1 ? "s" : "", " in $nrows block row", nrows>1 ? "s" : "")
38+
print_maps(io, A.maps)
39+
end
40+
function _show(io::IO, A::BlockDiagonalMap)
41+
n = length(A.maps)
42+
println(io, " with $n diagonal block map", n>1 ? "s" : "")
43+
print_maps(io, A.maps)
44+
end
45+
function _show(io::IO, J::UniformScalingMap)
46+
s = "$(J.λ)"
47+
print(io, " with scaling factor: $s")
48+
end
49+
function _show(io::IO, A::ScaledMap{T}) where {T}
50+
println(io, " with scale: $(A.λ) of")
51+
show(io, A.lmap)
52+
end
53+
54+
# helper functions
55+
function _show_typeof(io::IO, A::LinearMap{T}) where {T}
56+
Base.show_type_name(io, typeof(A).name)
57+
print(io, '{')
58+
show(io, T)
59+
print(io, '}')
60+
end
61+
62+
function print_maps(io::IO, maps::Tuple{Vararg{LinearMap}})
63+
n = length(maps)
64+
if get(io, :limit, true) && n > 10
65+
s = 1:5
66+
e = n-5:n
67+
if e[1] - s[end] > 1
68+
for i in s
69+
# print(io, ' ')
70+
show(io, maps[i])
71+
println(io, "")
72+
end
73+
print(io, "")
74+
for i in e
75+
println(io, "")
76+
show(io, maps[i])
77+
end
78+
else
79+
for i in 1:n-1
80+
# print(io, ' ')
81+
show(io, maps[i])
82+
println(io, "")
83+
end
84+
# print(io, ' ')
85+
show(io, last(maps))
86+
end
87+
else
88+
for i in 1:n-1
89+
# print(io, ' ')
90+
show(io, maps[i])
91+
println(io, "")
92+
end
93+
# print(io, ' ')
94+
show(io, last(maps))
95+
end
96+
end

src/transpose.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ MulStyle(A::Union{TransposeMap,AdjointMap}) = MulStyle(A.lmap)
1111
LinearAlgebra.transpose(A::TransposeMap) = A.lmap
1212
LinearAlgebra.adjoint(A::AdjointMap) = A.lmap
1313

14+
Base.parent(A::Union{AdjointMap,TransposeMap}) = A.lmap
15+
1416
"""
1517
transpose(A::LinearMap)
1618

src/uniformscalingmap.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ MulStyle(::UniformScalingMap) = FiveArg()
1515

1616
# properties
1717
Base.size(A::UniformScalingMap) = (A.M, A.M)
18+
Base.parent(A::UniformScalingMap) = A.λ
1819
Base.isreal(A::UniformScalingMap) = isreal(A.λ)
1920
LinearAlgebra.issymmetric(::UniformScalingMap) = true
2021
LinearAlgebra.ishermitian(A::UniformScalingMap) = isreal(A)

src/wrappedmap.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Base.:(==)(A::MatrixMap, B::MatrixMap) =
3232

3333
# properties
3434
Base.size(A::WrappedMap) = size(A.lmap)
35+
Base.parent(A::WrappedMap) = A.lmap
3536
LinearAlgebra.issymmetric(A::WrappedMap) = A._issymmetric
3637
LinearAlgebra.ishermitian(A::WrappedMap) = A._ishermitian
3738
LinearAlgebra.isposdef(A::WrappedMap) = A._isposdef

test/blockmap.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays, BenchmarkTools, Interactive
77
A12 = rand(elty, 10, n2)
88
v = rand(elty, 10)
99
L = @inferred hcat(LinearMap(A11), LinearMap(A12))
10+
@test parent(L) == (LinearMap(A11), LinearMap(A12))
11+
@test occursin("10×$(10+n2) LinearMaps.BlockMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), L))
1012
@test @inferred(LinearMaps.MulStyle(L)) === matrixstyle
1113
@test L isa LinearMaps.BlockMap{elty}
1214
if elty <: Complex
@@ -30,6 +32,10 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays, BenchmarkTools, Interactive
3032
x = rand(elty, 61)
3133
@test L isa LinearMaps.BlockMap{elty}
3234
@test L * x A * x
35+
L = @inferred hcat(I, I, I, LinearMap(A11), A11, A11, v, v, v, v)
36+
@test occursin("10×64 LinearMaps.BlockMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), L))
37+
L = @inferred hcat(I, I, I, LinearMap(A11), A11, A11, v, v, v, v, v, v, v)
38+
@test occursin("10×67 LinearMaps.BlockMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), L))
3339
A11 = rand(elty, 11, 10)
3440
A12 = rand(elty, 10, n2)
3541
@test_throws DimensionMismatch hcat(LinearMap(A11), LinearMap(A12))
@@ -45,6 +51,7 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays, BenchmarkTools, Interactive
4551
@test Matrix(L) A11
4652
A21 = rand(elty, 20, 10)
4753
L = @inferred vcat(LinearMap(A11), LinearMap(A21))
54+
@test occursin("30×10 LinearMaps.BlockMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), L))
4855
@test L isa LinearMaps.BlockMap{elty}
4956
@test @inferred(LinearMaps.MulStyle(L)) === matrixstyle
5057
@test (@which [A11; A21]).module != LinearMaps
@@ -193,6 +200,8 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays, BenchmarkTools, Interactive
193200
@test (@which cat(M1, M2, M3, M2, M1; dims=(1,2))).module != LinearMaps
194201
x = randn(elty, size(Md, 2))
195202
Bd = @inferred blockdiag(L1, L2, L3, L2, L1)
203+
@test parent(Bd) == (L1, L2, L3, L2, L1)
204+
@test occursin("25×39 LinearMaps.BlockDiagonalMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), Bd))
196205
@test Matrix(Bd) == Md
197206
@test convert(AbstractMatrix, Bd) isa SparseMatrixCSC
198207
@test sparse(Bd) == Md

test/composition.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays
2020
@test @inferred (A * F) * v == @inferred A * (F * v)
2121
@test @inferred A * (F * F) * v == @inferred A * (F * (F * v))
2222
F2 = F*F
23+
@test parent(F2) == (F, F)
2324
FC2 = FC*FC
2425
F4 = FC2 * F2
26+
@test occursin("10×10 LinearMaps.CompositeMap{$(eltype(F4))}", sprint((t, s) -> show(t, "text/plain", s), F4))
2527
@test length(F4.maps) == 4
2628
@test @inferred F4 * v == @inferred F * (F * (F * (F * v)))
2729
@test @inferred Matrix(M * transpose(M)) A * transpose(A)

test/functionmap.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ using Test, LinearMaps, LinearAlgebra, BenchmarkTools
1717
MyFT = @inferred LinearMap{ComplexF64}(myft, N) / sqrt(N)
1818
U = Matrix(MyFT) # will be a unitary matrix
1919
@test @inferred U'U Matrix{eltype(U)}(I, N, N)
20+
@test occursin("$N×$N LinearMaps.FunctionMap{$(eltype(MyFT))}", sprint((t, s) -> show(t, "text/plain", s), MyFT))
21+
@test parent(LinearMap{ComplexF64}(myft, N)) === (myft, nothing)
2022

2123
CS = @inferred LinearMap(cumsum, 2)
2224
@test size(CS) == (2, 2)
@@ -33,6 +35,7 @@ using Test, LinearMaps, LinearAlgebra, BenchmarkTools
3335
@test *(CS, v) == cv
3436
@test_throws ErrorException CS' * v
3537
CS = @inferred LinearMap(cumsum, x -> reverse(cumsum(reverse(x))), 10; ismutating=false)
38+
@test occursin("10×10 LinearMaps.FunctionMap{Float64}", sprint((t, s) -> show(t, "text/plain", s), CS))
3639
cv = cumsum(v)
3740
@test @inferred CS * v == cv
3841
@test @inferred *(CS, v) == cv

test/kronecker.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays
88
LA = LinearMap(A)
99
LB = LinearMap(B)
1010
LK = @inferred kron(LA, LB)
11+
@test parent(LK) == (LA, LB)
1112
@test_throws AssertionError LinearMaps.KroneckerMap{Float64}((LA, LB))
13+
@test occursin("6×6 LinearMaps.KroneckerMap{$(eltype(LK))}", sprint((t, s) -> show(t, "text/plain", s), LK))
1214
@test @inferred size(LK) == size(K)
1315
@test LinearMaps.MulStyle(LK) === LinearMaps.ThreeArg()
1416
for i in (1, 2)
@@ -68,6 +70,8 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays
6870
LA = LinearMap(A)
6971
LB = LinearMap(B)
7072
KS = @inferred kronsum(LA, B)
73+
@test parent(KS) == (LA, LB)
74+
@test occursin("6×6 LinearMaps.KroneckerSumMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), KS))
7175
@test_throws ArgumentError kronsum(LA, [B B]) # non-square map
7276
KSmat = kron(A, Matrix(I, 2, 2)) + kron(Matrix(I, 3, 3), B)
7377
@test Matrix(KS) Matrix(kron(A, LinearMap(I, 2)) + kron(LinearMap(I, 3), B))

test/linearcombination.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ using Test, LinearMaps, LinearAlgebra, BenchmarkTools
1111
n = 10
1212
L = sum(fill(CS!, n))
1313
@test_throws AssertionError LinearMaps.LinearCombination{Float64}((CS!, CS!))
14+
@test occursin("10×10 LinearMaps.LinearCombination{$(eltype(L))}", sprint((t, s) -> show(t, "text/plain", s), L))
15+
@test parent(L) == ntuple(_ -> CS!, 10)
1416
@test mul!(u, L, v) n * cumsum(v)
1517
b = @benchmarkable mul!($u, $L, $v, 2, 2)
1618
@test run(b, samples=5).allocs <= 1

0 commit comments

Comments
 (0)