diff --git a/docs/src/types.md b/docs/src/types.md index a5ab814c..81be9cc2 100644 --- a/docs/src/types.md +++ b/docs/src/types.md @@ -14,6 +14,12 @@ Abstract supertype LinearMaps.LinearMap ``` +Unwrapping function + +```@docs +Base.parent +``` + ### `FunctionMap` Type for wrapping an arbitrary function that is supposed to implement the diff --git a/src/LinearMaps.jl b/src/LinearMaps.jl index 3224a86a..f8cc42e6 100644 --- a/src/LinearMaps.jl +++ b/src/LinearMaps.jl @@ -48,6 +48,15 @@ Base.ndims(::LinearMap) = 2 Base.size(A::LinearMap, n) = (n==1 || n==2 ? size(A)[n] : error("LinearMap objects have only 2 dimensions")) Base.length(A::LinearMap) = size(A)[1] * size(A)[2] +""" + parent(A::LinearMap) + +Return the underlying "parent map". This parent map is what was passed as an argument to +the specific `LinearMap` constructor, including implicit constructors and up to implicit +promotion to a `LinearMap` subtype. The fallback is to return the input itself. +""" +Base.parent(A::LinearMap) = A + # check dimension consistency for multiplication A*B _iscompatible((A, B)) = size(A, 2) == size(B, 1) function check_dim_mul(A, B) @@ -235,6 +244,7 @@ include("functionmap.jl") # using a function as linear map include("blockmap.jl") # block linear maps include("kronecker.jl") # Kronecker product of linear maps include("conversion.jl") # conversion of linear maps to matrices +include("show.jl") # show methods for LinearMap objects """ LinearMap(A::LinearMap; kwargs...)::WrappedMap diff --git a/src/blockmap.jl b/src/blockmap.jl index 92516b27..d31281b9 100644 --- a/src/blockmap.jl +++ b/src/blockmap.jl @@ -17,6 +17,8 @@ BlockMap{T}(maps::As, rows::S) where {T,As<:Tuple{Vararg{LinearMap}},S} = BlockM MulStyle(A::BlockMap) = MulStyle(A.maps...) +Base.parent(A::BlockMap) = A.maps + """ rowcolranges(maps, rows) @@ -458,6 +460,10 @@ Base.cat Base.size(A::BlockDiagonalMap) = (last(A.rowranges[end]), last(A.colranges[end])) +MulStyle(A::BlockDiagonalMap) = MulStyle(A.maps...) + +Base.parent(A::BlockDiagonalMap) = A.maps + LinearAlgebra.issymmetric(A::BlockDiagonalMap) = all(issymmetric, A.maps) LinearAlgebra.ishermitian(A::BlockDiagonalMap{<:Real}) = all(issymmetric, A.maps) LinearAlgebra.ishermitian(A::BlockDiagonalMap) = all(ishermitian, A.maps) diff --git a/src/composition.jl b/src/composition.jl index eb70bdec..844382b8 100644 --- a/src/composition.jl +++ b/src/composition.jl @@ -17,6 +17,7 @@ CompositeMap{T}(maps::As) where {T, As<:Tuple{Vararg{LinearMap}}} = CompositeMap # basic methods Base.size(A::CompositeMap) = (size(A.maps[end], 1), size(A.maps[1], 2)) Base.isreal(A::CompositeMap) = all(isreal, A.maps) # sufficient but not necessary +Base.parent(A::CompositeMap) = A.maps # the following rules are sufficient but not necessary for (f, _f, g) in ((:issymmetric, :_issymmetric, :transpose), diff --git a/src/functionmap.jl b/src/functionmap.jl index 044a4983..8a7f97aa 100644 --- a/src/functionmap.jl +++ b/src/functionmap.jl @@ -21,16 +21,9 @@ FunctionMap{T}(f, M::Int; kwargs...) where {T} = FunctionMap{T}(f, nothi FunctionMap{T}(f, M::Int, N::Int; kwargs...) where {T} = FunctionMap{T}(f, nothing, M, N; kwargs...) FunctionMap{T}(f, fc, M::Int; kwargs...) where {T} = FunctionMap{T}(f, fc, M, M; kwargs...) -# show -function Base.show(io::IO, A::FunctionMap{T, F, Nothing}) where {T, F} - print(io, "LinearMaps.FunctionMap{$T}($(A.f), $(A.M), $(A.N); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))") -end -function Base.show(io::IO, A::FunctionMap{T}) where {T} - 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))") -end - # properties Base.size(A::FunctionMap) = (A.M, A.N) +Base.parent(A::FunctionMap) = (A.f, A.fc) LinearAlgebra.issymmetric(A::FunctionMap) = A._issymmetric LinearAlgebra.ishermitian(A::FunctionMap) = A._ishermitian LinearAlgebra.isposdef(A::FunctionMap) = A._isposdef diff --git a/src/kronecker.jl b/src/kronecker.jl index 8c513859..5837bdc0 100644 --- a/src/kronecker.jl +++ b/src/kronecker.jl @@ -88,6 +88,8 @@ Base.:(^)(A::MapOrMatrix, ::KronPower{p}) where {p} = Base.size(A::KroneckerMap) = map(*, size.(A.maps)...) +Base.parent(A::KroneckerMap) = A.maps + LinearAlgebra.issymmetric(A::KroneckerMap) = all(issymmetric, A.maps) LinearAlgebra.ishermitian(A::KroneckerMap{<:Real}) = issymmetric(A) LinearAlgebra.ishermitian(A::KroneckerMap) = all(ishermitian, A.maps) @@ -123,7 +125,7 @@ end if nb*ma < mb*na _unsafe_mul!(Y, B, Matrix(X*At)) else - _unsafe_mul!(Y, Matrix(B*X), At isa MatrixMap ? At.lmap : At.λ) + _unsafe_mul!(Y, Matrix(B*X), parent(At)) end return y end @@ -248,6 +250,7 @@ Base.:(^)(A::MapOrMatrix, ::KronSumPower{p}) where {p} = kronsum(ntuple(n -> con Base.size(A::KroneckerSumMap, i) = prod(size.(A.maps, i)) Base.size(A::KroneckerSumMap) = (size(A, 1), size(A, 2)) +Base.parent(A::KroneckerSumMap) = A.maps LinearAlgebra.issymmetric(A::KroneckerSumMap) = all(issymmetric, A.maps) LinearAlgebra.ishermitian(A::KroneckerSumMap{<:Real}) = all(issymmetric, A.maps) diff --git a/src/linearcombination.jl b/src/linearcombination.jl index 526a330a..13fa9abc 100644 --- a/src/linearcombination.jl +++ b/src/linearcombination.jl @@ -18,6 +18,7 @@ MulStyle(A::LinearCombination) = MulStyle(A.maps...) # basic methods Base.size(A::LinearCombination) = size(A.maps[1]) +Base.parent(A::LinearCombination) = A.maps LinearAlgebra.issymmetric(A::LinearCombination) = all(issymmetric, A.maps) # sufficient but not necessary LinearAlgebra.ishermitian(A::LinearCombination) = all(ishermitian, A.maps) # sufficient but not necessary LinearAlgebra.isposdef(A::LinearCombination) = all(isposdef, A.maps) # sufficient but not necessary diff --git a/src/scaledmap.jl b/src/scaledmap.jl index db44fad8..ef80f59b 100644 --- a/src/scaledmap.jl +++ b/src/scaledmap.jl @@ -11,14 +11,9 @@ end ScaledMap(λ::S, lmap::A) where {S<:RealOrComplex,A<:LinearMap} = ScaledMap{Base.promote_op(*, S, eltype(lmap))}(λ, lmap) -# show -function Base.show(io::IO, A::ScaledMap{T}) where {T} - println(io, "LinearMaps.ScaledMap{$T}, scale = $(A.λ)") - show(io, A.lmap) -end - # basic methods Base.size(A::ScaledMap) = size(A.lmap) +Base.parent(A::ScaledMap) = (A.λ, A.lmap) Base.isreal(A::ScaledMap) = isreal(A.λ) && isreal(A.lmap) LinearAlgebra.issymmetric(A::ScaledMap) = issymmetric(A.lmap) LinearAlgebra.ishermitian(A::ScaledMap) = ishermitian(A.lmap) diff --git a/src/show.jl b/src/show.jl new file mode 100644 index 00000000..2aca5477 --- /dev/null +++ b/src/show.jl @@ -0,0 +1,96 @@ +# summary +function Base.summary(io::IO, A::LinearMap) + print(io, Base.dims2string(size(A))) + print(io, ' ') + _show_typeof(io, A) +end + +# show +Base.show(io::IO, A::LinearMap) = (summary(io, A); _show(io, A)) +_show(io::IO, ::LinearMap) = nothing +function _show(io::IO, A::FunctionMap{T,F,Nothing}) where {T,F} + print(io, "($(A.f); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))") +end +function _show(io::IO, A::FunctionMap) + print(io, "($(A.f), $(A.fc); ismutating=$(A._ismutating), issymmetric=$(A._issymmetric), ishermitian=$(A._ishermitian), isposdef=$(A._isposdef))") +end +function _show(io::IO, A::Union{CompositeMap,LinearCombination,KroneckerMap,KroneckerSumMap}) + n = length(A.maps) + println(io, " with $n map", n>1 ? "s" : "", ":") + print_maps(io, A.maps) +end +function _show(io::IO, A::Union{AdjointMap,TransposeMap,WrappedMap}) + print(io, " of ") + L = A.lmap + if A isa MatrixMap + # summary(io, L) + # println(io, ":") + # Base.print_matrix(io, L) + print(io, typeof(L)) + else + show(io, L) + end +end +function _show(io::IO, A::BlockMap) + nrows = length(A.rows) + n = length(A.maps) + println(io, " with $n block map", n>1 ? "s" : "", " in $nrows block row", nrows>1 ? "s" : "") + print_maps(io, A.maps) +end +function _show(io::IO, A::BlockDiagonalMap) + n = length(A.maps) + println(io, " with $n diagonal block map", n>1 ? "s" : "") + print_maps(io, A.maps) +end +function _show(io::IO, J::UniformScalingMap) + s = "$(J.λ)" + print(io, " with scaling factor: $s") +end +function _show(io::IO, A::ScaledMap{T}) where {T} + println(io, " with scale: $(A.λ) of") + show(io, A.lmap) +end + +# helper functions +function _show_typeof(io::IO, A::LinearMap{T}) where {T} + Base.show_type_name(io, typeof(A).name) + print(io, '{') + show(io, T) + print(io, '}') +end + +function print_maps(io::IO, maps::Tuple{Vararg{LinearMap}}) + n = length(maps) + if get(io, :limit, true) && n > 10 + s = 1:5 + e = n-5:n + if e[1] - s[end] > 1 + for i in s + # print(io, ' ') + show(io, maps[i]) + println(io, "") + end + print(io, "⋮") + for i in e + println(io, "") + show(io, maps[i]) + end + else + for i in 1:n-1 + # print(io, ' ') + show(io, maps[i]) + println(io, "") + end + # print(io, ' ') + show(io, last(maps)) + end + else + for i in 1:n-1 + # print(io, ' ') + show(io, maps[i]) + println(io, "") + end + # print(io, ' ') + show(io, last(maps)) + end +end diff --git a/src/transpose.jl b/src/transpose.jl index ed631582..e4e30361 100644 --- a/src/transpose.jl +++ b/src/transpose.jl @@ -11,6 +11,8 @@ MulStyle(A::Union{TransposeMap,AdjointMap}) = MulStyle(A.lmap) LinearAlgebra.transpose(A::TransposeMap) = A.lmap LinearAlgebra.adjoint(A::AdjointMap) = A.lmap +Base.parent(A::Union{AdjointMap,TransposeMap}) = A.lmap + """ transpose(A::LinearMap) diff --git a/src/uniformscalingmap.jl b/src/uniformscalingmap.jl index 823c7700..32d83081 100644 --- a/src/uniformscalingmap.jl +++ b/src/uniformscalingmap.jl @@ -15,6 +15,7 @@ MulStyle(::UniformScalingMap) = FiveArg() # properties Base.size(A::UniformScalingMap) = (A.M, A.M) +Base.parent(A::UniformScalingMap) = A.λ Base.isreal(A::UniformScalingMap) = isreal(A.λ) LinearAlgebra.issymmetric(::UniformScalingMap) = true LinearAlgebra.ishermitian(A::UniformScalingMap) = isreal(A) diff --git a/src/wrappedmap.jl b/src/wrappedmap.jl index 3fe34fe5..9a5f77ef 100644 --- a/src/wrappedmap.jl +++ b/src/wrappedmap.jl @@ -32,6 +32,7 @@ Base.:(==)(A::MatrixMap, B::MatrixMap) = # properties Base.size(A::WrappedMap) = size(A.lmap) +Base.parent(A::WrappedMap) = A.lmap LinearAlgebra.issymmetric(A::WrappedMap) = A._issymmetric LinearAlgebra.ishermitian(A::WrappedMap) = A._ishermitian LinearAlgebra.isposdef(A::WrappedMap) = A._isposdef diff --git a/test/blockmap.jl b/test/blockmap.jl index 07e22858..7582c851 100644 --- a/test/blockmap.jl +++ b/test/blockmap.jl @@ -7,6 +7,8 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays, BenchmarkTools, Interactive A12 = rand(elty, 10, n2) v = rand(elty, 10) L = @inferred hcat(LinearMap(A11), LinearMap(A12)) + @test parent(L) == (LinearMap(A11), LinearMap(A12)) + @test occursin("10×$(10+n2) LinearMaps.BlockMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), L)) @test @inferred(LinearMaps.MulStyle(L)) === matrixstyle @test L isa LinearMaps.BlockMap{elty} if elty <: Complex @@ -30,6 +32,10 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays, BenchmarkTools, Interactive x = rand(elty, 61) @test L isa LinearMaps.BlockMap{elty} @test L * x ≈ A * x + L = @inferred hcat(I, I, I, LinearMap(A11), A11, A11, v, v, v, v) + @test occursin("10×64 LinearMaps.BlockMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), L)) + L = @inferred hcat(I, I, I, LinearMap(A11), A11, A11, v, v, v, v, v, v, v) + @test occursin("10×67 LinearMaps.BlockMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), L)) A11 = rand(elty, 11, 10) A12 = rand(elty, 10, n2) @test_throws DimensionMismatch hcat(LinearMap(A11), LinearMap(A12)) @@ -45,6 +51,7 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays, BenchmarkTools, Interactive @test Matrix(L) ≈ A11 A21 = rand(elty, 20, 10) L = @inferred vcat(LinearMap(A11), LinearMap(A21)) + @test occursin("30×10 LinearMaps.BlockMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), L)) @test L isa LinearMaps.BlockMap{elty} @test @inferred(LinearMaps.MulStyle(L)) === matrixstyle @test (@which [A11; A21]).module != LinearMaps @@ -193,6 +200,8 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays, BenchmarkTools, Interactive @test (@which cat(M1, M2, M3, M2, M1; dims=(1,2))).module != LinearMaps x = randn(elty, size(Md, 2)) Bd = @inferred blockdiag(L1, L2, L3, L2, L1) + @test parent(Bd) == (L1, L2, L3, L2, L1) + @test occursin("25×39 LinearMaps.BlockDiagonalMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), Bd)) @test Matrix(Bd) == Md @test convert(AbstractMatrix, Bd) isa SparseMatrixCSC @test sparse(Bd) == Md diff --git a/test/composition.jl b/test/composition.jl index 27921595..3a538254 100644 --- a/test/composition.jl +++ b/test/composition.jl @@ -20,8 +20,10 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays @test @inferred (A * F) * v == @inferred A * (F * v) @test @inferred A * (F * F) * v == @inferred A * (F * (F * v)) F2 = F*F + @test parent(F2) == (F, F) FC2 = FC*FC F4 = FC2 * F2 + @test occursin("10×10 LinearMaps.CompositeMap{$(eltype(F4))}", sprint((t, s) -> show(t, "text/plain", s), F4)) @test length(F4.maps) == 4 @test @inferred F4 * v == @inferred F * (F * (F * (F * v))) @test @inferred Matrix(M * transpose(M)) ≈ A * transpose(A) diff --git a/test/functionmap.jl b/test/functionmap.jl index 7e7a0146..9f277350 100644 --- a/test/functionmap.jl +++ b/test/functionmap.jl @@ -17,6 +17,8 @@ using Test, LinearMaps, LinearAlgebra, BenchmarkTools MyFT = @inferred LinearMap{ComplexF64}(myft, N) / sqrt(N) U = Matrix(MyFT) # will be a unitary matrix @test @inferred U'U ≈ Matrix{eltype(U)}(I, N, N) + @test occursin("$N×$N LinearMaps.FunctionMap{$(eltype(MyFT))}", sprint((t, s) -> show(t, "text/plain", s), MyFT)) + @test parent(LinearMap{ComplexF64}(myft, N)) === (myft, nothing) CS = @inferred LinearMap(cumsum, 2) @test size(CS) == (2, 2) @@ -33,6 +35,7 @@ using Test, LinearMaps, LinearAlgebra, BenchmarkTools @test *(CS, v) == cv @test_throws ErrorException CS' * v CS = @inferred LinearMap(cumsum, x -> reverse(cumsum(reverse(x))), 10; ismutating=false) + @test occursin("10×10 LinearMaps.FunctionMap{Float64}", sprint((t, s) -> show(t, "text/plain", s), CS)) cv = cumsum(v) @test @inferred CS * v == cv @test @inferred *(CS, v) == cv diff --git a/test/kronecker.jl b/test/kronecker.jl index c55d0b50..f9031a62 100644 --- a/test/kronecker.jl +++ b/test/kronecker.jl @@ -8,7 +8,9 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays LA = LinearMap(A) LB = LinearMap(B) LK = @inferred kron(LA, LB) + @test parent(LK) == (LA, LB) @test_throws AssertionError LinearMaps.KroneckerMap{Float64}((LA, LB)) + @test occursin("6×6 LinearMaps.KroneckerMap{$(eltype(LK))}", sprint((t, s) -> show(t, "text/plain", s), LK)) @test @inferred size(LK) == size(K) @test LinearMaps.MulStyle(LK) === LinearMaps.ThreeArg() for i in (1, 2) @@ -68,6 +70,8 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays LA = LinearMap(A) LB = LinearMap(B) KS = @inferred kronsum(LA, B) + @test parent(KS) == (LA, LB) + @test occursin("6×6 LinearMaps.KroneckerSumMap{$elty}", sprint((t, s) -> show(t, "text/plain", s), KS)) @test_throws ArgumentError kronsum(LA, [B B]) # non-square map KSmat = kron(A, Matrix(I, 2, 2)) + kron(Matrix(I, 3, 3), B) @test Matrix(KS) ≈ Matrix(kron(A, LinearMap(I, 2)) + kron(LinearMap(I, 3), B)) diff --git a/test/linearcombination.jl b/test/linearcombination.jl index 0325c531..bff12f7c 100644 --- a/test/linearcombination.jl +++ b/test/linearcombination.jl @@ -11,6 +11,8 @@ using Test, LinearMaps, LinearAlgebra, BenchmarkTools n = 10 L = sum(fill(CS!, n)) @test_throws AssertionError LinearMaps.LinearCombination{Float64}((CS!, CS!)) + @test occursin("10×10 LinearMaps.LinearCombination{$(eltype(L))}", sprint((t, s) -> show(t, "text/plain", s), L)) + @test parent(L) == ntuple(_ -> CS!, 10) @test mul!(u, L, v) ≈ n * cumsum(v) b = @benchmarkable mul!($u, $L, $v, 2, 2) @test run(b, samples=5).allocs <= 1 diff --git a/test/linearmaps.jl b/test/linearmaps.jl index 5628aae7..7ea67f2a 100644 --- a/test/linearmaps.jl +++ b/test/linearmaps.jl @@ -21,34 +21,6 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays, BenchmarkTools @test length(M) == length(A) end - Av = A * v - AV = A * V - - @testset "mul! and *" begin - for w in (vec(u), u) - @test M * v == Av - @test N * v == Av - @test @inferred mul!(copy(w), M, v) == mul!(copy(w), A, v) - b = @benchmarkable mul!($w, $M, $v) - @test run(b, samples=3).allocs == 0 - @test @inferred mul!(copy(w), N, v) == mul!(copy(w), A, v) - - # mat-vec-mul - @test @inferred mul!(copy(w), M, v, 0, 0) == zero(w) - @test @inferred mul!(copy(w), M, v, 0, 1) == w - @test @inferred mul!(copy(w), M, v, 0, β) == β * w - @test @inferred mul!(copy(w), M, v, 1, 1) ≈ Av + w - @test @inferred mul!(copy(w), M, v, 1, β) ≈ Av + β * w - @test @inferred mul!(copy(w), M, v, α, 1) ≈ α * Av + w - @test @inferred mul!(copy(w), M, v, α, β) ≈ α * Av + β * w - end - - # test mat-mat-mul! - @test @inferred mul!(copy(W), M, V, α, β) ≈ α * AV + β * W - @test @inferred mul!(copy(W), M, V) ≈ AV - @test typeof(M * V) <: LinearMap - end - @testset "dimension checking" begin w = vec(u) @test_throws DimensionMismatch M * similar(v, length(v) + 1) @@ -70,11 +42,12 @@ struct SimpleComplexFunctionMap <: LinearMap{Complex{Float64}} N::Int end Base.size(A::Union{SimpleFunctionMap,SimpleComplexFunctionMap}) = (A.N, A.N) -Base.:(*)(A::Union{SimpleFunctionMap,SimpleComplexFunctionMap}, v::Vector) = A.f(v) +Base.:(*)(A::Union{SimpleFunctionMap,SimpleComplexFunctionMap}, v::AbstractVector) = A.f(v) LinearAlgebra.mul!(y::AbstractVector, A::Union{SimpleFunctionMap,SimpleComplexFunctionMap}, x::AbstractVector) = copyto!(y, *(A, x)) @testset "new LinearMap type" begin F = SimpleFunctionMap(cumsum, 10) + @test parent(F) === F FC = SimpleComplexFunctionMap(cumsum, 10) @test @inferred ndims(F) == 2 @test @inferred size(F, 1) == 10 @@ -83,10 +56,12 @@ LinearAlgebra.mul!(y::AbstractVector, A::Union{SimpleFunctionMap,SimpleComplexFu @test @inferred !ishermitian(F) @test @inferred !ishermitian(FC) @test @inferred !isposdef(F) - v = rand(ComplexF64, 10) - w = similar(v) - mul!(w, F, v) - @test w == F * v + @test occursin("10×10 SimpleFunctionMap{$(eltype(F))}", sprint((t, s) -> show(t, "text/plain", s), F)) + @test occursin("10×10 SimpleComplexFunctionMap{$(eltype(FC))}", sprint((t, s) -> show(t, "text/plain", s), FC)) + α = rand(ComplexF64); β = rand(ComplexF64) + v = rand(ComplexF64, 10); V = rand(ComplexF64, 10, 3) + w = rand(ComplexF64, 10); W = rand(ComplexF64, 10, 3) + @test mul!(w, F, v) === w == F * v @test_throws ErrorException F' * v @test_throws ErrorException transpose(F) * v @test_throws ErrorException mul!(w, adjoint(FC), v) @@ -95,6 +70,13 @@ LinearAlgebra.mul!(y::AbstractVector, A::Union{SimpleFunctionMap,SimpleComplexFu L = LowerTriangular(ones(10, 10)) @test FM == L @test F * v ≈ L * v + # generic 5-arg mul! and matrix-mul! + @test mul!(copy(w), F, v, α, β) ≈ L*v*α + w*β + @test mul!(copy(w), F, v, 0, β) ≈ w*β + @test mul!(copy(W), F, V) ≈ L*V + @test mul!(copy(W), F, V, α, β) ≈ L*V*α + W*β + @test mul!(copy(W), F, V, 0, β) ≈ W*β + Fs = sparse(F) @test SparseMatrixCSC(F) == Fs == L @test Fs isa SparseMatrixCSC diff --git a/test/scaledmap.jl b/test/scaledmap.jl index c87aec54..2948b908 100644 --- a/test/scaledmap.jl +++ b/test/scaledmap.jl @@ -8,6 +8,8 @@ using Test, LinearMaps, LinearAlgebra, BenchmarkTools # real case α = float(π) B = @inferred α * A + @test occursin("7×7 LinearMaps.ScaledMap{Float64} with scale: $α", sprint((t, s) -> show(t, "text/plain", s), B)) + @test parent(B) == (α, A) x = rand(N) @test @inferred size(B) == size(A) diff --git a/test/transpose.jl b/test/transpose.jl index 823428ec..08e25f87 100644 --- a/test/transpose.jl +++ b/test/transpose.jl @@ -1,59 +1,6 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays @testset "transpose/adjoint" begin - A = 2 * rand(ComplexF64, (20, 10)) .- 1 - v = rand(ComplexF64, 10) - w = rand(ComplexF64, 20) - V = rand(ComplexF64, 10, 3) - W = rand(ComplexF64, 20, 3) - Av = A * v - AV = A * V - M = @inferred LinearMap(A) - N = @inferred LinearMap(M) - - @test @inferred M' * w == A' * w - @test @inferred mul!(copy(V), adjoint(M), W) ≈ A' * W - @test @inferred transpose(M) * w == transpose(A) * w - @test @inferred transpose(LinearMap(transpose(M))) * v == A * v - @test @inferred LinearMap(M')' * v == A * v - @test @inferred(transpose(transpose(M))) === M - @test @inferred(adjoint(adjoint(M))) === M - Mherm = @inferred LinearMap(A'A) - @test @inferred ishermitian(Mherm) - @test @inferred !issymmetric(Mherm) - @test @inferred !issymmetric(transpose(Mherm)) - @test @inferred ishermitian(transpose(Mherm)) - @test @inferred ishermitian(Mherm') - @test @inferred isposdef(Mherm) - @test @inferred isposdef(transpose(Mherm)) - @test @inferred isposdef(adjoint(Mherm)) - @test @inferred !(transpose(M) == adjoint(M)) - @test @inferred !(adjoint(M) == transpose(M)) - @test @inferred transpose(M') * v ≈ transpose(A') * v - @test @inferred transpose(LinearMap(M')) * v ≈ transpose(A') * v - @test @inferred LinearMap(transpose(M))' * v ≈ transpose(A)' * v - @test @inferred transpose(LinearMap(transpose(M))) * v ≈ Av - @test @inferred adjoint(LinearMap(adjoint(M))) * v ≈ Av - - @test @inferred mul!(copy(w), transpose(LinearMap(M')), v) ≈ transpose(A') * v - @test @inferred mul!(copy(w), LinearMap(transpose(M))', v) ≈ transpose(A)' * v - @test @inferred mul!(copy(w), transpose(LinearMap(transpose(M))), v) ≈ Av - @test @inferred mul!(copy(w), adjoint(LinearMap(adjoint(M))), v) ≈ Av - @test @inferred mul!(copy(W), transpose(LinearMap(M')), V) ≈ transpose(A') * V - @test @inferred mul!(copy(W), LinearMap(transpose(M))', V) ≈ transpose(A)' * V - @test @inferred mul!(copy(W), transpose(LinearMap(transpose(M))), V) ≈ AV - @test @inferred mul!(copy(W), adjoint(LinearMap(adjoint(M))), V) ≈ AV - @test @inferred mul!(copy(V), transpose(M), W) ≈ transpose(A) * W - @test @inferred mul!(copy(V), adjoint(M), W) ≈ A' * W - - B = @inferred LinearMap(Symmetric(rand(10, 10))) - @test transpose(B) == B - @test B == transpose(B) - - B = @inferred LinearMap(Hermitian(rand(ComplexF64, 10, 10))) - @test adjoint(B) == B - @test B == B' - CS = @inferred LinearMap{ComplexF64}(cumsum, x -> reverse(cumsum(reverse(x))), 10; ismutating=false) for transform in (adjoint, transpose) @test transform(CS) != CS @@ -61,6 +8,10 @@ using Test, LinearMaps, LinearAlgebra, SparseArrays @test transform(transform(CS)) == CS @test LinearMaps.MulStyle(transform(CS)) === LinearMaps.MulStyle(CS) end + @test occursin("10×10 LinearMaps.TransposeMap{$(eltype(CS))}", sprint((t, s) -> show(t, "text/plain", s), transpose(CS))) + @test occursin("10×10 LinearMaps.AdjointMap{$(eltype(CS))}", sprint((t, s) -> show(t, "text/plain", s), adjoint(CS))) + @test parent(transpose(CS)) === CS + @test parent(adjoint(CS)) === CS @test !(transpose(CS) == adjoint(CS)) @test !(adjoint(CS) == transpose(CS)) M = Matrix(CS) diff --git a/test/uniformscalingmap.jl b/test/uniformscalingmap.jl index f83715d4..6ae4f304 100644 --- a/test/uniformscalingmap.jl +++ b/test/uniformscalingmap.jl @@ -10,6 +10,8 @@ using Test, LinearMaps, LinearAlgebra, BenchmarkTools v = rand(ComplexF64, 10) w = similar(v) Id = @inferred LinearMap(I, 10) + @test occursin("10×10 LinearMaps.UniformScalingMap{Bool}", sprint((t, s) -> show(t, "text/plain", s), Id)) + @test parent(Id) == true @test_throws ErrorException LinearMaps.UniformScalingMap(1, 10, 20) @test_throws ErrorException LinearMaps.UniformScalingMap(1, (10, 20)) @test size(Id) == (10, 10) diff --git a/test/wrappedmap.jl b/test/wrappedmap.jl index ac04dc75..5cb26d5c 100644 --- a/test/wrappedmap.jl +++ b/test/wrappedmap.jl @@ -6,6 +6,8 @@ using Test, LinearMaps, LinearAlgebra SA = A'A + I SB = B'B + I L = @inferred LinearMap{Float64}(A) + @test occursin("10×20 LinearMaps.WrappedMap{Float64}", sprint((t, s) -> show(t, "text/plain", s), L)) + @test parent(L) === A MA = @inferred LinearMap(SA) MB = @inferred LinearMap(SB) @test eltype(Matrix{Complex{Float32}}(LinearMap(A))) <: Complex @@ -15,4 +17,60 @@ using Test, LinearMaps, LinearAlgebra @test @inferred !issymmetric(MB) @test @inferred isposdef(MA) @test @inferred isposdef(MB) + + A = 2 * rand(ComplexF64, (20, 10)) .- 1 + v = rand(ComplexF64, 10) + w = rand(ComplexF64, 20) + u = rand(ComplexF64, 20, 1) + V = rand(ComplexF64, 10, 3) + W = rand(ComplexF64, 20, 3) + Av = A * v + AV = A * V + M = @inferred LinearMap(A) + N = @inferred LinearMap(M) + + @test @inferred M' * w == A' * w + @test @inferred mul!(copy(V), adjoint(M), W) ≈ A' * W + @test @inferred transpose(M) * w == transpose(A) * w + @test @inferred transpose(LinearMap(transpose(M))) * v == A * v + @test @inferred LinearMap(M')' * v == A * v + @test @inferred(transpose(transpose(M))) === M + @test @inferred(adjoint(adjoint(M))) === M + Mherm = @inferred LinearMap(A'A) + @test @inferred ishermitian(Mherm) + @test @inferred !issymmetric(Mherm) + @test @inferred !issymmetric(transpose(Mherm)) + @test @inferred ishermitian(transpose(Mherm)) + @test @inferred ishermitian(Mherm') + @test @inferred isposdef(Mherm) + @test @inferred isposdef(transpose(Mherm)) + @test @inferred isposdef(adjoint(Mherm)) + @test @inferred !(transpose(M) == adjoint(M)) + @test @inferred !(adjoint(M) == transpose(M)) + @test @inferred transpose(M') * v ≈ transpose(A') * v + @test @inferred transpose(LinearMap(M')) * v ≈ transpose(A') * v + @test @inferred LinearMap(transpose(M))' * v ≈ transpose(A)' * v + @test @inferred transpose(LinearMap(transpose(M))) * v ≈ Av + @test @inferred adjoint(LinearMap(adjoint(M))) * v ≈ Av + + for w in (vec(u), u) + @test @inferred mul!(copy(w), transpose(LinearMap(M')), v) ≈ transpose(A') * v + @test @inferred mul!(copy(w), LinearMap(transpose(M))', v) ≈ transpose(A)' * v + @test @inferred mul!(copy(w), transpose(LinearMap(transpose(M))), v) ≈ Av + @test @inferred mul!(copy(w), adjoint(LinearMap(adjoint(M))), v) ≈ Av + end + @test @inferred mul!(copy(W), transpose(LinearMap(M')), V) ≈ transpose(A') * V + @test @inferred mul!(copy(W), LinearMap(transpose(M))', V) ≈ transpose(A)' * V + @test @inferred mul!(copy(W), transpose(LinearMap(transpose(M))), V) ≈ AV + @test @inferred mul!(copy(W), adjoint(LinearMap(adjoint(M))), V) ≈ AV + @test @inferred mul!(copy(V), transpose(M), W) ≈ transpose(A) * W + @test @inferred mul!(copy(V), adjoint(M), W) ≈ A' * W + + B = @inferred LinearMap(Symmetric(rand(10, 10))) + @test transpose(B) == B + @test B == transpose(B) + + B = @inferred LinearMap(Hermitian(rand(ComplexF64, 10, 10))) + @test adjoint(B) == B + @test B == B' end