From a0d558d90e59fd473713a55b3b0c3c7d93917729 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 1 Sep 2020 22:41:07 +0200 Subject: [PATCH 01/17] draft for fillmap --- src/fillmap.jl | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/fillmap.jl diff --git a/src/fillmap.jl b/src/fillmap.jl new file mode 100644 index 00000000..c0e5c5a4 --- /dev/null +++ b/src/fillmap.jl @@ -0,0 +1,59 @@ +struct FillMap{T} <: LinearMaps.LinearMap{T} + value::T + size::Dims{2} +end + +# properties +Base.size(A::FillMap) = A.size +MulStyle(A::FillMap) = FiveArg() +LinearAlgebra.issymmetric(A::FillMap) = A.size[1] == A.size[2] +LinearAlgebra.ishermitian(A::FillMap) = isreal(A) && A.size[1] == A.size[2] +LinearAlgebra.isposdef(A::FillMap) = false +Base.:(==)(A::FillMap, B::FillMap) = A.value == B.value && A.size == B.size + +LinearAlgebra.adjoint(A::FillMap) = FillMap(adjoint(value), revert(A.size)) +LinearAlgebra.transpose(A::FillMap) = FillMap(transpose(value), revert(A.size)) + +function LinearMaps.A_mul_B!(y::AbstractVecOrMat, A::FillMap, x::AbstractVector) + checkbounds(y, A, x) + if iszero(A.value) + fill!(y, zero(eltype(y))) + else + temp = sum(x) + fill!(y, A.value*temp) + end + return y +end + +Base.@propagate_inbounds function mul!(y::AbstractVecOrMat, A::FillMap, x::AbstractVector, α::Number, β::Number) + @boundscheck checkbounds(y, A, x) + if iszero(α) + !isone(β) && rmul!(y, β) + return y + else + temp = sum(x)*α + if iszero(β) + y .+= temp + elseif isone(β) + y .= y .+ temp + else + y .= y.*β .+ temp + end + return y +end + +Base.:(+)(A::FillMap, B::FillMap) = A.size == B.size ? FillMap(A.value + B.value, A.size) : throw(DimensionMismatch()) +Base.:(-)(A::FillMap, B::FillMap) = A.size == B.size ? FillMap(A.value - B.value, A.size) : throw(DimensionMismatch()) +Base.:(*)(λ::Number, A::FillMap) = FillMap(λ*A.value, size(A)) +Base.:(*)(A::FillMap, λ::Number) = FillMap(A.value*λ, size(A)) +Base.:(*)(J::UniformScaling, A::FillMap) = FillMap(J.λ*A.value, size(A)) +Base.:(*)(A::FillMap, J::UniformScaling) = FillMap(A.value*J.λ, size(A)) +Base.:(*)(J::LinearMaps.UniformScalingMap, A::FillMap) = FillMap(J.λ*A.value, size(A)) +Base.:(*)(A::FillMap, J::LinearMaps.UniformScalingMap) = FillMap(A.value*J.λ, size(A)) + +function Base.:(*)(A::FillMap, B::FillMap) + mA, nA = size(A) + mB, nB = size(B) + nA != mB && throw(DimensionMismatch()) + return FillMap(A.value*B.value*nA, (mA, nB)) +end From 0bad7615efa8c81216b22d83aacd5a7605029e18 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Mon, 7 Sep 2020 21:08:20 +0200 Subject: [PATCH 02/17] fix typos, remove obsolete code --- src/fillmap.jl | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/src/fillmap.jl b/src/fillmap.jl index c0e5c5a4..e3b7e1f0 100644 --- a/src/fillmap.jl +++ b/src/fillmap.jl @@ -1,5 +1,5 @@ struct FillMap{T} <: LinearMaps.LinearMap{T} - value::T + λ::T size::Dims{2} end @@ -9,51 +9,39 @@ MulStyle(A::FillMap) = FiveArg() LinearAlgebra.issymmetric(A::FillMap) = A.size[1] == A.size[2] LinearAlgebra.ishermitian(A::FillMap) = isreal(A) && A.size[1] == A.size[2] LinearAlgebra.isposdef(A::FillMap) = false -Base.:(==)(A::FillMap, B::FillMap) = A.value == B.value && A.size == B.size +Base.:(==)(A::FillMap, B::FillMap) = A.λ == B.λ && A.size == B.size -LinearAlgebra.adjoint(A::FillMap) = FillMap(adjoint(value), revert(A.size)) -LinearAlgebra.transpose(A::FillMap) = FillMap(transpose(value), revert(A.size)) +LinearAlgebra.adjoint(A::FillMap) = FillMap(adjoint(A.λ), revert(A.size)) +LinearAlgebra.transpose(A::FillMap) = FillMap(transpose(A.λ), revert(A.size)) -function LinearMaps.A_mul_B!(y::AbstractVecOrMat, A::FillMap, x::AbstractVector) - checkbounds(y, A, x) - if iszero(A.value) - fill!(y, zero(eltype(y))) - else - temp = sum(x) - fill!(y, A.value*temp) - end - return y +function _unsafe_mul!(y::AbstractVecOrMat, A::FillMap, x::AbstractVector) + return fill!(y, iszero(A.λ) ? zero(eltype(y)) : A.λ*sum(x)) end -Base.@propagate_inbounds function mul!(y::AbstractVecOrMat, A::FillMap, x::AbstractVector, α::Number, β::Number) - @boundscheck checkbounds(y, A, x) +function _unsafe_mul!(y::AbstractVecOrMat, A::FillMap, x::AbstractVector, α::Number, β::Number) if iszero(α) !isone(β) && rmul!(y, β) return y else - temp = sum(x)*α + temp = A.λ * sum(x) * α if iszero(β) - y .+= temp + y .= temp elseif isone(β) - y .= y .+ temp + y .+= temp else - y .= y.*β .+ temp + y .= y .* β .+ temp end return y end -Base.:(+)(A::FillMap, B::FillMap) = A.size == B.size ? FillMap(A.value + B.value, A.size) : throw(DimensionMismatch()) -Base.:(-)(A::FillMap, B::FillMap) = A.size == B.size ? FillMap(A.value - B.value, A.size) : throw(DimensionMismatch()) -Base.:(*)(λ::Number, A::FillMap) = FillMap(λ*A.value, size(A)) -Base.:(*)(A::FillMap, λ::Number) = FillMap(A.value*λ, size(A)) -Base.:(*)(J::UniformScaling, A::FillMap) = FillMap(J.λ*A.value, size(A)) -Base.:(*)(A::FillMap, J::UniformScaling) = FillMap(A.value*J.λ, size(A)) -Base.:(*)(J::LinearMaps.UniformScalingMap, A::FillMap) = FillMap(J.λ*A.value, size(A)) -Base.:(*)(A::FillMap, J::LinearMaps.UniformScalingMap) = FillMap(A.value*J.λ, size(A)) +Base.:(+)(A::FillMap, B::FillMap) = A.size == B.size ? FillMap(A.λ + B.λ, A.size) : throw(DimensionMismatch()) +Base.:(-)(A::FillMap) = FillMap(-A.λ, A.size) +Base.:(*)(λ::Number, A::FillMap) = FillMap(λ * A.λ, size(A)) +Base.:(*)(A::FillMap, λ::Number) = FillMap(A.λ * λ, size(A)) function Base.:(*)(A::FillMap, B::FillMap) mA, nA = size(A) mB, nB = size(B) nA != mB && throw(DimensionMismatch()) - return FillMap(A.value*B.value*nA, (mA, nB)) + return FillMap(A.λ*B.λ*nA, (mA, nB)) end From 90c600defd8ba777e6f033ab294059ed75ee8f4a Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 8 Sep 2020 17:09:10 +0200 Subject: [PATCH 03/17] add tests --- test/fillmap.jl | 30 ++++++++++++++++++++++++++++++ test/runtests.jl | 2 ++ 2 files changed, 32 insertions(+) create mode 100644 test/fillmap.jl diff --git a/test/fillmap.jl b/test/fillmap.jl new file mode 100644 index 00000000..efc1b9c6 --- /dev/null +++ b/test/fillmap.jl @@ -0,0 +1,30 @@ +using LinearMaps, LinearAlgebra, Test + +@testset "filled maps" begin + M, N = 2, 3 + α = rand() + β = rand() + μ = rand() + for λ in (true, false, 3, μ, μ + 2im) + L = LinearMap(λ, (M, N)) + A = fill(λ, (M, N)) + x = rand(typeof(λ) <: Real ? Float64 : ComplexF64, 3) + X = rand(typeof(λ) <: Real ? Float64 : ComplexF64, 3, 3) + w = similar(x, 2) + W = similar(X, 2, 3) + @test size(L) == (M, N) + @test adjoint(L) == LinearMap(adjoint(λ), (3,2)) + @test transpose(L) == LinearMap(λ, (3,2)) + @test Matrix(L) == A + @test L * x ≈ A * x + @test mul!(w, L, x) ≈ A * x + @test mul!(W, L, X) ≈ A * X + @test mul!(copy(w), L, x, α, β) ≈ A * x * α + w * β + @test mul!(copy(W), L, X, α, β) ≈ A * X * α + W * β + end + @test issymmetric(LinearMap(μ + 0im, (3, 3))) + @test LinearMap(μ, (M, N)) + LinearMap(α, (M, N)) == LinearMap(μ + α, (M, N)) + @test LinearMap(μ, (M, N)) - LinearMap(α, (M, N)) == LinearMap(μ - α, (M, N)) + @test α*LinearMap(μ, (M, N)) == LinearMap(α * μ, (M, N)) + @test LinearMap(μ, (M, N))*α == LinearMap(μ * α, (M, N)) +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index b3507f29..e316b318 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -30,3 +30,5 @@ include("kronecker.jl") include("conversion.jl") include("left.jl") + +include("fillmap.jl") From 611ada9dc8d78538b122c1dba245f6e1c0099f38 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 8 Sep 2020 17:09:23 +0200 Subject: [PATCH 04/17] add conversion --- src/conversion.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/conversion.jl b/src/conversion.jl index ba80130f..c229e61e 100644 --- a/src/conversion.jl +++ b/src/conversion.jl @@ -171,3 +171,6 @@ function SparseArrays.sparse(L::KroneckerSumMap) IB = sparse(Diagonal(ones(Bool, size(B, 1)))) return kron(convert(AbstractMatrix, A), IB) + kron(IA, convert(AbstractMatrix, B)) end + +# FillMap +Base.Matrix(A::FillMap) = fill(A.λ, size(A)) From 92cab571ac7d8f39c50589f4ec3d861852e5abf3 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 8 Sep 2020 17:13:13 +0200 Subject: [PATCH 05/17] general edits --- src/LinearMaps.jl | 3 +++ src/fillmap.jl | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/LinearMaps.jl b/src/LinearMaps.jl index cc9b146d..f1247240 100644 --- a/src/LinearMaps.jl +++ b/src/LinearMaps.jl @@ -239,6 +239,7 @@ include("composition.jl") # composition of linear maps include("functionmap.jl") # using a function as linear map include("blockmap.jl") # block linear maps include("kronecker.jl") # Kronecker product of linear maps +include("fillmap.jl") # linear maps representing constantly filled matrices include("conversion.jl") # conversion of linear maps to matrices include("show.jl") # show methods for LinearMap objects @@ -281,6 +282,8 @@ LinearMap(f, M::Int; kwargs...) = LinearMap{Float64}(f, M; kwargs...) LinearMap(f, M::Int, N::Int; kwargs...) = LinearMap{Float64}(f, M, N; kwargs...) LinearMap(f, fc, M::Int; kwargs...) = LinearMap{Float64}(f, fc, M; kwargs...) LinearMap(f, fc, M::Int, N::Int; kwargs...) = LinearMap{Float64}(f, fc, M, N; kwargs...) +LinearMap(λ::Number, M::Int, N::Int) = LinearMap(λ, (M, N)) +LinearMap(λ::Number, dims::Dims{2}) = FillMap(λ, dims) LinearMap{T}(A::MapOrMatrix; kwargs...) where {T} = WrappedMap{T}(A; kwargs...) LinearMap{T}(f, args...; kwargs...) where {T} = FunctionMap{T}(f, args...; kwargs...) diff --git a/src/fillmap.jl b/src/fillmap.jl index e3b7e1f0..5171793b 100644 --- a/src/fillmap.jl +++ b/src/fillmap.jl @@ -1,6 +1,11 @@ struct FillMap{T} <: LinearMaps.LinearMap{T} λ::T size::Dims{2} + function FillMap(λ::T, dims) where {T} + all(d -> d >= 0, dims) || throw(ArgumentError("dims of FillMap must be non-negative")) + promote_type(T, typeof(λ)) == T || throw(InexactError()) + return new{T}(λ, dims) + end end # properties @@ -8,11 +13,16 @@ Base.size(A::FillMap) = A.size MulStyle(A::FillMap) = FiveArg() LinearAlgebra.issymmetric(A::FillMap) = A.size[1] == A.size[2] LinearAlgebra.ishermitian(A::FillMap) = isreal(A) && A.size[1] == A.size[2] -LinearAlgebra.isposdef(A::FillMap) = false +LinearAlgebra.isposdef(A::FillMap) = (size(A, 1) == size(A, 2) == 1 && isposdef(A.λ)) Base.:(==)(A::FillMap, B::FillMap) = A.λ == B.λ && A.size == B.size -LinearAlgebra.adjoint(A::FillMap) = FillMap(adjoint(A.λ), revert(A.size)) -LinearAlgebra.transpose(A::FillMap) = FillMap(transpose(A.λ), revert(A.size)) +LinearAlgebra.adjoint(A::FillMap) = FillMap(adjoint(A.λ), reverse(A.size)) +LinearAlgebra.transpose(A::FillMap) = FillMap(transpose(A.λ), reverse(A.size)) + +function Base.:(*)(A::FillMap, x::AbstractVector) + T = typeof(oneunit(eltype(A)) * (zero(eltype(x)) + zero(eltype(x)))) + return fill(iszero(A.λ) ? zero(T) : A.λ*sum(x), A.size[1]) +end function _unsafe_mul!(y::AbstractVecOrMat, A::FillMap, x::AbstractVector) return fill!(y, iszero(A.λ) ? zero(eltype(y)) : A.λ*sum(x)) @@ -31,6 +41,7 @@ function _unsafe_mul!(y::AbstractVecOrMat, A::FillMap, x::AbstractVector, α::Nu else y .= y .* β .+ temp end + end return y end @@ -38,6 +49,8 @@ Base.:(+)(A::FillMap, B::FillMap) = A.size == B.size ? FillMap(A.λ + B.λ, A.si Base.:(-)(A::FillMap) = FillMap(-A.λ, A.size) Base.:(*)(λ::Number, A::FillMap) = FillMap(λ * A.λ, size(A)) Base.:(*)(A::FillMap, λ::Number) = FillMap(A.λ * λ, size(A)) +Base.:(*)(λ::RealOrComplex, A::FillMap) = FillMap(λ * A.λ, size(A)) +Base.:(*)(A::FillMap, λ::RealOrComplex) = FillMap(A.λ * λ, size(A)) function Base.:(*)(A::FillMap, B::FillMap) mA, nA = size(A) From a22e705374b56ad37486d4fcd5e1b2354a34cfdc Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 16 Sep 2020 08:39:56 +0200 Subject: [PATCH 06/17] conversion fix --- src/conversion.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversion.jl b/src/conversion.jl index c229e61e..8588a5c3 100644 --- a/src/conversion.jl +++ b/src/conversion.jl @@ -173,4 +173,4 @@ function SparseArrays.sparse(L::KroneckerSumMap) end # FillMap -Base.Matrix(A::FillMap) = fill(A.λ, size(A)) +Base.Matrix{T}(A::FillMap) where {T} = fill(T(A.λ), size(A)) From 62883c661946c13dc6237fd422203e1efdc93290 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 16 Sep 2020 08:42:29 +0200 Subject: [PATCH 07/17] minor fix --- src/fillmap.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fillmap.jl b/src/fillmap.jl index 5171793b..fab76672 100644 --- a/src/fillmap.jl +++ b/src/fillmap.jl @@ -1,4 +1,4 @@ -struct FillMap{T} <: LinearMaps.LinearMap{T} +struct FillMap{T} <: LinearMap{T} λ::T size::Dims{2} function FillMap(λ::T, dims) where {T} From b7c2051bfd247b41bfd5a2396606f8c89183fa82 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Mon, 21 Sep 2020 12:16:16 +0200 Subject: [PATCH 08/17] minor simplifications --- src/fillmap.jl | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/fillmap.jl b/src/fillmap.jl index fab76672..b97d706c 100644 --- a/src/fillmap.jl +++ b/src/fillmap.jl @@ -1,9 +1,8 @@ struct FillMap{T} <: LinearMap{T} λ::T size::Dims{2} - function FillMap(λ::T, dims) where {T} - all(d -> d >= 0, dims) || throw(ArgumentError("dims of FillMap must be non-negative")) - promote_type(T, typeof(λ)) == T || throw(InexactError()) + function FillMap(λ::T, dims::Dims{2}) where {T} + all(>=(0), dims) || throw(ArgumentError("dims of FillMap must be non-negative")) return new{T}(λ, dims) end end @@ -12,7 +11,7 @@ end Base.size(A::FillMap) = A.size MulStyle(A::FillMap) = FiveArg() LinearAlgebra.issymmetric(A::FillMap) = A.size[1] == A.size[2] -LinearAlgebra.ishermitian(A::FillMap) = isreal(A) && A.size[1] == A.size[2] +LinearAlgebra.ishermitian(A::FillMap) = isreal(A.λ) && A.size[1] == A.size[2] LinearAlgebra.isposdef(A::FillMap) = (size(A, 1) == size(A, 2) == 1 && isposdef(A.λ)) Base.:(==)(A::FillMap, B::FillMap) = A.λ == B.λ && A.size == B.size @@ -31,7 +30,6 @@ end function _unsafe_mul!(y::AbstractVecOrMat, A::FillMap, x::AbstractVector, α::Number, β::Number) if iszero(α) !isone(β) && rmul!(y, β) - return y else temp = A.λ * sum(x) * α if iszero(β) @@ -53,8 +51,6 @@ Base.:(*)(λ::RealOrComplex, A::FillMap) = FillMap(λ * A.λ, size(A)) Base.:(*)(A::FillMap, λ::RealOrComplex) = FillMap(A.λ * λ, size(A)) function Base.:(*)(A::FillMap, B::FillMap) - mA, nA = size(A) - mB, nB = size(B) - nA != mB && throw(DimensionMismatch()) - return FillMap(A.λ*B.λ*nA, (mA, nB)) + check_dim_mul(A, B) + return FillMap(A.λ*B.λ*nA, (size(A, 1), size(B, 2))) end From 2cf8c331aae6d8ababdd57358eae56ec9f91ea1e Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Mon, 5 Oct 2020 16:52:25 +0200 Subject: [PATCH 09/17] minor fix for Julia v1.0 --- src/fillmap.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fillmap.jl b/src/fillmap.jl index b97d706c..113a91c3 100644 --- a/src/fillmap.jl +++ b/src/fillmap.jl @@ -2,7 +2,7 @@ struct FillMap{T} <: LinearMap{T} λ::T size::Dims{2} function FillMap(λ::T, dims::Dims{2}) where {T} - all(>=(0), dims) || throw(ArgumentError("dims of FillMap must be non-negative")) + (dims[1]>=0 && dims[2]>=0) || throw(ArgumentError("dims of FillMap must be non-negative")) return new{T}(λ, dims) end end @@ -52,5 +52,5 @@ Base.:(*)(A::FillMap, λ::RealOrComplex) = FillMap(A.λ * λ, size(A)) function Base.:(*)(A::FillMap, B::FillMap) check_dim_mul(A, B) - return FillMap(A.λ*B.λ*nA, (size(A, 1), size(B, 2))) + return FillMap(A.λ*B.λ*size(A, 2), (size(A, 1), size(B, 2))) end From 40037b22f0f7d4c2450041043ff0bb13e05e5599 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 6 Oct 2020 14:46:53 +0200 Subject: [PATCH 10/17] improve coverage --- src/LinearMaps.jl | 2 +- test/fillmap.jl | 23 ++++++++++++++++------- test/numbertypes.jl | 1 + 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/LinearMaps.jl b/src/LinearMaps.jl index f1247240..e63d6ec0 100644 --- a/src/LinearMaps.jl +++ b/src/LinearMaps.jl @@ -282,7 +282,7 @@ LinearMap(f, M::Int; kwargs...) = LinearMap{Float64}(f, M; kwargs...) LinearMap(f, M::Int, N::Int; kwargs...) = LinearMap{Float64}(f, M, N; kwargs...) LinearMap(f, fc, M::Int; kwargs...) = LinearMap{Float64}(f, fc, M; kwargs...) LinearMap(f, fc, M::Int, N::Int; kwargs...) = LinearMap{Float64}(f, fc, M, N; kwargs...) -LinearMap(λ::Number, M::Int, N::Int) = LinearMap(λ, (M, N)) +LinearMap(λ::Number, M::Int, N::Int) = FillMap(λ, (M, N)) LinearMap(λ::Number, dims::Dims{2}) = FillMap(λ, dims) LinearMap{T}(A::MapOrMatrix; kwargs...) where {T} = WrappedMap{T}(A; kwargs...) diff --git a/test/fillmap.jl b/test/fillmap.jl index efc1b9c6..515babc9 100644 --- a/test/fillmap.jl +++ b/test/fillmap.jl @@ -2,16 +2,16 @@ using LinearMaps, LinearAlgebra, Test @testset "filled maps" begin M, N = 2, 3 - α = rand() - β = rand() μ = rand() for λ in (true, false, 3, μ, μ + 2im) L = LinearMap(λ, (M, N)) + @test L == LinearMap(λ, M, N) + @test LinearMaps.MulStyle(L) === LinearMaps.FiveArg() A = fill(λ, (M, N)) x = rand(typeof(λ) <: Real ? Float64 : ComplexF64, 3) - X = rand(typeof(λ) <: Real ? Float64 : ComplexF64, 3, 3) + X = rand(typeof(λ) <: Real ? Float64 : ComplexF64, 3, 4) w = similar(x, 2) - W = similar(X, 2, 3) + W = similar(X, 2, 4) @test size(L) == (M, N) @test adjoint(L) == LinearMap(adjoint(λ), (3,2)) @test transpose(L) == LinearMap(λ, (3,2)) @@ -19,12 +19,21 @@ using LinearMaps, LinearAlgebra, Test @test L * x ≈ A * x @test mul!(w, L, x) ≈ A * x @test mul!(W, L, X) ≈ A * X - @test mul!(copy(w), L, x, α, β) ≈ A * x * α + w * β - @test mul!(copy(W), L, X, α, β) ≈ A * X * α + W * β + for α in (true, false, 1, 0, randn()), β in (true, false, 1, 0, randn()) + @test mul!(copy(w), L, x, α, β) ≈ fill(λ * sum(x) * α, M) + w * β + @test mul!(copy(W), L, X, α, β) ≈ λ * reduce(vcat, sum(X, dims=1) for _ in 1:2) * α + W * β + end end - @test issymmetric(LinearMap(μ + 0im, (3, 3))) + @test issymmetric(LinearMap(μ + 1im, (3, 3))) + @test ishermitian(LinearMap(μ + 0im, (3, 3))) + @test isposdef(LinearMap(μ, (1,1))) == isposdef(μ) + @test !isposdef(LinearMap(μ, (3,3))) + α = rand() + β = rand() @test LinearMap(μ, (M, N)) + LinearMap(α, (M, N)) == LinearMap(μ + α, (M, N)) @test LinearMap(μ, (M, N)) - LinearMap(α, (M, N)) == LinearMap(μ - α, (M, N)) @test α*LinearMap(μ, (M, N)) == LinearMap(α * μ, (M, N)) @test LinearMap(μ, (M, N))*α == LinearMap(μ * α, (M, N)) + @test LinearMap(μ, (M, N))*LinearMap(μ, (N, M)) == LinearMap(μ^2*N, (M, M)) + @test Matrix(LinearMap(μ, (M, N))*LinearMap(μ, (N, M))) == fill(μ, (M, N))*fill(μ, (N, M)) end \ No newline at end of file diff --git a/test/numbertypes.jl b/test/numbertypes.jl index 38439299..95ab2940 100644 --- a/test/numbertypes.jl +++ b/test/numbertypes.jl @@ -45,6 +45,7 @@ using Test, LinearMaps, LinearAlgebra, Quaternions @test M == (L * L * γ) * β == (L * L * α) * β == (L * L * α) * β.λ @test length(M.maps) == 3 @test M.maps[1].λ == γ*β.λ + @test γ*LinearMap(γ, (3, 4)) == LinearMap(γ^2, (3, 4)) == LinearMap(γ, (3, 4))*γ # exercise non-RealOrComplex scalar operations @test Array(γ * (L'*L)) ≈ γ * (A'*A) # CompositeMap From 6145ec42cfa544740312c64f19e024e5d12b9205 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Tue, 6 Oct 2020 21:37:24 +0200 Subject: [PATCH 11/17] fix test --- test/fillmap.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fillmap.jl b/test/fillmap.jl index 515babc9..be5ef3c9 100644 --- a/test/fillmap.jl +++ b/test/fillmap.jl @@ -35,5 +35,5 @@ using LinearMaps, LinearAlgebra, Test @test α*LinearMap(μ, (M, N)) == LinearMap(α * μ, (M, N)) @test LinearMap(μ, (M, N))*α == LinearMap(μ * α, (M, N)) @test LinearMap(μ, (M, N))*LinearMap(μ, (N, M)) == LinearMap(μ^2*N, (M, M)) - @test Matrix(LinearMap(μ, (M, N))*LinearMap(μ, (N, M))) == fill(μ, (M, N))*fill(μ, (N, M)) + @test Matrix(LinearMap(μ, (M, N))*LinearMap(μ, (N, M))) ≈ fill(μ, (M, N))*fill(μ, (N, M)) end \ No newline at end of file From 90559a4093a49e28295702050794463a7a444f88 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 7 Oct 2020 16:50:00 +0200 Subject: [PATCH 12/17] add show method --- src/show.jl | 3 +++ test/fillmap.jl | 1 + 2 files changed, 4 insertions(+) diff --git a/src/show.jl b/src/show.jl index 3b5ca5c2..2342d1de 100644 --- a/src/show.jl +++ b/src/show.jl @@ -39,6 +39,9 @@ end function _show(io::IO, A::ScaledMap{T}, i) where {T} " with scale: $(A.λ) of\n" * map_show(io, A.lmap, i+2) end +function _show(io::IO, A::FillMap{T}) where {T} + println(io, " with fill value: $(A.λ)") +end # helper functions function _show_typeof(A::LinearMap{T}) where {T} diff --git a/test/fillmap.jl b/test/fillmap.jl index be5ef3c9..a14afad1 100644 --- a/test/fillmap.jl +++ b/test/fillmap.jl @@ -6,6 +6,7 @@ using LinearMaps, LinearAlgebra, Test for λ in (true, false, 3, μ, μ + 2im) L = LinearMap(λ, (M, N)) @test L == LinearMap(λ, M, N) + @test occursin("$M×$N LinearMaps.FillMap{$(typeof(λ))}", sprint((t, s) -> show(t, "text/plain", s), L)) @test LinearMaps.MulStyle(L) === LinearMaps.FiveArg() A = fill(λ, (M, N)) x = rand(typeof(λ) <: Real ? Float64 : ComplexF64, 3) From d88e0fc3837ddc4ea14d408fa0bc1c466b4675bd Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 7 Oct 2020 16:50:29 +0200 Subject: [PATCH 13/17] include documentation --- docs/src/types.md | 4 ++++ src/LinearMaps.jl | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/src/types.md b/docs/src/types.md index 7dafc147..79f7d369 100644 --- a/docs/src/types.md +++ b/docs/src/types.md @@ -84,6 +84,10 @@ Base.cat SparseArrays.blockdiag ``` +### `FillMap`` + +Type for lazily representing constantly filled matrices. + ## Methods ### Multiplication methods diff --git a/src/LinearMaps.jl b/src/LinearMaps.jl index e63d6ec0..e82c286b 100644 --- a/src/LinearMaps.jl +++ b/src/LinearMaps.jl @@ -247,11 +247,14 @@ include("show.jl") # show methods for LinearMap objects LinearMap(A::LinearMap; kwargs...)::WrappedMap LinearMap(A::AbstractMatrix; kwargs...)::WrappedMap LinearMap(J::UniformScaling, M::Int)::UniformScalingMap + LinearMap(λ::Number, M::Int, N::Int) = FillMap(λ, (M, N))::FillMap + LinearMap(λ::Number, dims::Dims{2}) = FillMap(λ, dims)::FillMap LinearMap{T=Float64}(f, [fc,], M::Int, N::Int = M; kwargs...)::FunctionMap Construct a linear map object, either from an existing `LinearMap` or `AbstractMatrix` `A`, with the purpose of redefining its properties via the keyword arguments `kwargs`; -a `UniformScaling` object `J` with specified (square) dimension `M`; or +a `UniformScaling` object `J` with specified (square) dimension `M`; from a `Number` +object to lazily represent filled matrices; or from a function or callable object `f`. In the latter case, one also needs to specify the size of the equivalent matrix representation `(M, N)`, i.e., for functions `f` acting on length `N` vectors and producing length `M` vectors (with default value `N=M`). From 8a9fc16813cce9f8ae15b85f993bbd43aee0c11e Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 7 Oct 2020 18:10:20 +0200 Subject: [PATCH 14/17] improve coverage --- test/fillmap.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fillmap.jl b/test/fillmap.jl index a14afad1..c2e04f84 100644 --- a/test/fillmap.jl +++ b/test/fillmap.jl @@ -6,7 +6,7 @@ using LinearMaps, LinearAlgebra, Test for λ in (true, false, 3, μ, μ + 2im) L = LinearMap(λ, (M, N)) @test L == LinearMap(λ, M, N) - @test occursin("$M×$N LinearMaps.FillMap{$(typeof(λ))}", sprint((t, s) -> show(t, "text/plain", s), L)) + @test occursin("$M×$N LinearMaps.FillMap{$(typeof(λ))} with fill value: $λ", sprint((t, s) -> show(t, "text/plain", s), L)) @test LinearMaps.MulStyle(L) === LinearMaps.FiveArg() A = fill(λ, (M, N)) x = rand(typeof(λ) <: Real ? Float64 : ComplexF64, 3) From b344a978dd55a8cee66409dc92831bbfdc07fcf0 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Thu, 8 Oct 2020 18:33:01 +0200 Subject: [PATCH 15/17] fix fillmap show --- src/show.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/show.jl b/src/show.jl index 2342d1de..514ac39d 100644 --- a/src/show.jl +++ b/src/show.jl @@ -39,8 +39,8 @@ end function _show(io::IO, A::ScaledMap{T}, i) where {T} " with scale: $(A.λ) of\n" * map_show(io, A.lmap, i+2) end -function _show(io::IO, A::FillMap{T}) where {T} - println(io, " with fill value: $(A.λ)") +function _show(io::IO, A::FillMap{T}, _) where {T} + " with fill value: $(A.λ)" end # helper functions From f6cd91ea5ea46721e1f6040dbafbbb67340c4906 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Thu, 10 Dec 2020 11:17:40 +0100 Subject: [PATCH 16/17] final fixes --- docs/src/index.md | 79 +++++++++++++++++++++++---------------------- docs/src/related.md | 33 ++++++++----------- docs/src/types.md | 44 +++++++++++++------------ src/LinearMaps.jl | 3 +- src/fillmap.jl | 9 ++++++ test/fillmap.jl | 30 ++++++++--------- test/numbertypes.jl | 2 +- 7 files changed, 104 insertions(+), 96 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index c75adebf..df1b43aa 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -16,23 +16,24 @@ Let A = LinearMap(rand(10, 10)) B = LinearMap(cumsum, reverse∘cumsum∘reverse, 10) - + be a matrix- and function-based linear map, respectively. Then the following code just works, indistinguishably from the case when `A` and `B` are both `AbstractMatrix`-typed objects. -``` -3.0A + 2B -A*B' -[A B; B A] -kron(A, B) -``` + 3.0A + 2B + A + I + A*B' + [A B; B A] + kron(A, B) The `LinearMap` type and corresponding methods combine well with the following packages: + * [Arpack.jl](https://github.com/JuliaLinearAlgebra/Arpack.jl): iterative eigensolver `eigs` and SVD `svds`; * [IterativeSolvers.jl](https://github.com/JuliaMath/IterativeSolvers.jl): iterative solvers, eigensolvers, and SVD; -* [KrylovKit.jl](https://github.com/Jutho/KrylovKit.jl): Krylov-based algorithms for linear problems, singular value and eigenvalue problems +* [KrylovKit.jl](https://github.com/Jutho/KrylovKit.jl): Krylov-based algorithms for linear + problems, singular value and eigenvalue problems * [TSVD.jl](https://github.com/andreasnoack/TSVD.jl): truncated SVD `tsvd`. ```julia @@ -95,34 +96,34 @@ operator in the special case of a square matrix). The LinearMaps package provides the following functionality: -1. A `LinearMap` type that shares with the `AbstractMatrix` type that it - responds to the functions `size`, `eltype`, `isreal`, `issymmetric`, - `ishermitian` and `isposdef`, `transpose` and `adjoint` and multiplication - with a vector using both `*` or the in-place version `mul!`. Linear algebra - functions that use duck-typing for their arguments can handle `LinearMap` - objects similar to `AbstractMatrix` objects, provided that they can be - written using the above methods. Unlike `AbstractMatrix` types, `LinearMap` - objects cannot be indexed, neither using `getindex` or `setindex!`. - -2. A single function `LinearMap` that acts as a general purpose - constructor (though it is only an abstract type) and allows to construct - linear map objects from functions, or to wrap objects of type - `AbstractMatrix` or `LinearMap`. The latter functionality is useful to - (re)define the properties (`isreal`, `issymmetric`, `ishermitian`, - `isposdef`) of the existing matrix or linear map. - -3. A framework for combining objects of type `LinearMap` and of type - `AbstractMatrix` using linear combinations, transposition, composition, - concatenation and Kronecker product/sums, - where the linear map resulting from these operations is never explicitly - evaluated but only its matrix-vector product is defined (i.e. lazy - evaluation). The matrix-vector product is written to minimize memory - allocation by using a minimal number of temporary vectors. There is full - support for the in-place version `mul!`, which should be preferred for - higher efficiency in critical algorithms. In addition, it tries to recognize - the properties of combinations of linear maps. In particular, compositions - such as `A'*A` for arbitrary `A` or even `A'*B*C*B'*A` with arbitrary `A` - and `B` and positive definite `C` are recognized as being positive definite - and hermitian. In case a certain property of the resulting `LinearMap` - object is not correctly inferred, the `LinearMap` method can be called to - redefine the properties. +1. A `LinearMap` type that shares with the `AbstractMatrix` type that it + responds to the functions `size`, `eltype`, `isreal`, `issymmetric`, + `ishermitian` and `isposdef`, `transpose` and `adjoint` and multiplication + with a vector using both `*` or the in-place version `mul!`. Linear algebra + functions that use duck-typing for their arguments can handle `LinearMap` + objects similar to `AbstractMatrix` objects, provided that they can be + written using the above methods. Unlike `AbstractMatrix` types, `LinearMap` + objects cannot be indexed, neither using `getindex` or `setindex!`. + +2. A single function `LinearMap` that acts as a general purpose + constructor (though it is only an abstract type) and allows to construct + linear map objects from functions, or to wrap objects of type + `AbstractMatrix` or `LinearMap`. The latter functionality is useful to + (re)define the properties (`isreal`, `issymmetric`, `ishermitian`, + `isposdef`) of the existing matrix or linear map. + +3. A framework for combining objects of type `LinearMap` and of type + `AbstractMatrix` using linear combinations, transposition, composition, + concatenation and Kronecker product/sums, + where the linear map resulting from these operations is never explicitly + evaluated but only its matrix-vector product is defined (i.e. lazy + evaluation). The matrix-vector product is written to minimize memory + allocation by using a minimal number of temporary vectors. There is full + support for the in-place version `mul!`, which should be preferred for + higher efficiency in critical algorithms. In addition, it tries to recognize + the properties of combinations of linear maps. In particular, compositions + such as `A'*A` for arbitrary `A` or even `A'*B*C*B'*A` with arbitrary `A` + and `B` and positive definite `C` are recognized as being positive definite + and hermitian. In case a certain property of the resulting `LinearMap` + object is not correctly inferred, the `LinearMap` method can be called to + redefine the properties. diff --git a/docs/src/related.md b/docs/src/related.md index 85e8d12a..2701488c 100644 --- a/docs/src/related.md +++ b/docs/src/related.md @@ -3,30 +3,25 @@ The following open-source packages provide similar or even extended functionality as `LinearMaps.jl`. -* [`Spot`: A linear-operator toolbox for Matlab](https://github.com/mpf/spot), - which seems to have heavily inspired the Julia package - [`LinearOperators.jl`](https://github.com/JuliaSmoothOptimizers/LinearOperators.jl) - and the Python package [`PyLops`](https://github.com/equinor/pylops) - -* [`fastmat`: fast linear transforms in Python](https://pypi.org/project/fastmat/) - -* [`FunctionOperators.jl`](https://github.com/hakkelt/FunctionOperators.jl) - and [`LinearMapsAA.jl`](https://github.com/JeffFessler/LinearMapsAA.jl) - also support mappings between `Array`s, inspired by the `fatrix` object type in the - [Matlab version of the Michigan Image Reconstruction Toolbox (MIRT)](https://github.com/JeffFessler/mirt). +* [`Spot`: A linear-operator toolbox for Matlab](https://github.com/mpf/spot), + which seems to have heavily inspired the Julia package + [`LinearOperators.jl`](https://github.com/JuliaSmoothOptimizers/LinearOperators.jl) + and the Python package [`PyLops`](https://github.com/equinor/pylops) +* [`fastmat`: fast linear transforms in Python](https://pypi.org/project/fastmat/) +* [`FunctionOperators.jl`](https://github.com/hakkelt/FunctionOperators.jl) + and [`LinearMapsAA.jl`](https://github.com/JeffFessler/LinearMapsAA.jl) + also support mappings between `Array`s, inspired by the `fatrix` object type in the + [Matlab version of the Michigan Image Reconstruction Toolbox (MIRT)](https://github.com/JeffFessler/mirt). As for lazy array manipulation (like addition, composition, Kronecker products and concatenation), there exist further related packages in the Julia ecosystem: -* [`LazyArrays.jl`](https://github.com/JuliaArrays/LazyArrays.jl) - -* [`BlockArrays.jl`](https://github.com/JuliaArrays/BlockArrays.jl) - -* [`BlockDiagonals.jl`](https://github.com/invenia/BlockDiagonals.jl) - -* [`Kronecker.jl`](https://github.com/MichielStock/Kronecker.jl) +* [`LazyArrays.jl`](https://github.com/JuliaArrays/LazyArrays.jl) +* [`BlockArrays.jl`](https://github.com/JuliaArrays/BlockArrays.jl) +* [`BlockDiagonals.jl`](https://github.com/invenia/BlockDiagonals.jl) +* [`Kronecker.jl`](https://github.com/MichielStock/Kronecker.jl) +* [`FillArrays.jl`](https://github.com/JuliaArrays/FillArrays.jl) -* [`FillArrays.jl`](https://github.com/JuliaArrays/FillArrays.jl) Since these packages provide types that are subtypes of Julia `Base`'s `AbstractMatrix` type, objects of those types can be wrapped by a `LinearMap` and freely mixed with, for instance, function-based linear maps. The same applies to custom matrix types as provided, for instance, diff --git a/docs/src/types.md b/docs/src/types.md index 79f7d369..a8faa1d4 100644 --- a/docs/src/types.md +++ b/docs/src/types.md @@ -88,6 +88,10 @@ SparseArrays.blockdiag Type for lazily representing constantly filled matrices. +```@docs +LinearMaps.FillMap +``` + ## Methods ### Multiplication methods @@ -107,28 +111,28 @@ as in the usual matrix case: `transpose(A) * x` and `mul!(y, A', x)`, for instan ### Conversion methods -* `Array`, `Matrix` and associated `convert` methods +* `Array`, `Matrix` and associated `convert` methods - Create a dense matrix representation of the `LinearMap` object, by - multiplying it with the successive basis vectors. This is mostly for testing - purposes or if you want to have the explicit matrix representation of a - linear map for which you only have a function definition (e.g. to be able to - use its `transpose` or `adjoint`). This way, one may conveniently make `A` - act on the columns of a matrix `X`, instead of interpreting `A * X` as a - composed linear map: `Matrix(A * X)`. For generic code, that is supposed to - handle both `A::AbstractMatrix` and `A::LinearMap`, it is recommended to use - `convert(Matrix, A*X)`. + Create a dense matrix representation of the `LinearMap` object, by + multiplying it with the successive basis vectors. This is mostly for testing + purposes or if you want to have the explicit matrix representation of a + linear map for which you only have a function definition (e.g. to be able to + use its `transpose` or `adjoint`). This way, one may conveniently make `A` + act on the columns of a matrix `X`, instead of interpreting `A * X` as a + composed linear map: `Matrix(A * X)`. For generic code, that is supposed to + handle both `A::AbstractMatrix` and `A::LinearMap`, it is recommended to use + `convert(Matrix, A*X)`. -* `convert(Abstract[Matrix/Array], A::LinearMap)` +* `convert(Abstract[Matrix/Array], A::LinearMap)` - Create an `AbstractMatrix` representation of the `LinearMap`. This falls - back to `Matrix(A)`, but avoids explicit construction in case the `LinearMap` - object is matrix-based. + Create an `AbstractMatrix` representation of the `LinearMap`. This falls + back to `Matrix(A)`, but avoids explicit construction in case the `LinearMap` + object is matrix-based. -* `SparseArrays.sparse(A::LinearMap)` and `convert(SparseMatrixCSC, A::LinearMap)` +* `SparseArrays.sparse(A::LinearMap)` and `convert(SparseMatrixCSC, A::LinearMap)` - Create a sparse matrix representation of the `LinearMap` object, by - multiplying it with the successive basis vectors. This is mostly for testing - purposes or if you want to have the explicit sparse matrix representation of - a linear map for which you only have a function definition (e.g. to be able - to use its `transpose` or `adjoint`). + Create a sparse matrix representation of the `LinearMap` object, by + multiplying it with the successive basis vectors. This is mostly for testing + purposes or if you want to have the explicit sparse matrix representation of + a linear map for which you only have a function definition (e.g. to be able + to use its `transpose` or `adjoint`). diff --git a/src/LinearMaps.jl b/src/LinearMaps.jl index e82c286b..026895ba 100644 --- a/src/LinearMaps.jl +++ b/src/LinearMaps.jl @@ -2,6 +2,7 @@ module LinearMaps export LinearMap export ⊗, kronsum, ⊕ +export FillMap using LinearAlgebra import LinearAlgebra: mul! @@ -285,8 +286,6 @@ LinearMap(f, M::Int; kwargs...) = LinearMap{Float64}(f, M; kwargs...) LinearMap(f, M::Int, N::Int; kwargs...) = LinearMap{Float64}(f, M, N; kwargs...) LinearMap(f, fc, M::Int; kwargs...) = LinearMap{Float64}(f, fc, M; kwargs...) LinearMap(f, fc, M::Int, N::Int; kwargs...) = LinearMap{Float64}(f, fc, M, N; kwargs...) -LinearMap(λ::Number, M::Int, N::Int) = FillMap(λ, (M, N)) -LinearMap(λ::Number, dims::Dims{2}) = FillMap(λ, dims) LinearMap{T}(A::MapOrMatrix; kwargs...) where {T} = WrappedMap{T}(A; kwargs...) LinearMap{T}(f, args...; kwargs...) where {T} = FunctionMap{T}(f, args...; kwargs...) diff --git a/src/fillmap.jl b/src/fillmap.jl index 113a91c3..c415740f 100644 --- a/src/fillmap.jl +++ b/src/fillmap.jl @@ -1,3 +1,10 @@ +""" + FillMap(λ, (m, n))::FillMap + FillMap(λ, m, n)::FillMap + +Construct a (lazy) representation of an operator whose matrix representation +would be an m×n-matrix filled constantly with the value `λ`. +""" struct FillMap{T} <: LinearMap{T} λ::T size::Dims{2} @@ -7,6 +14,8 @@ struct FillMap{T} <: LinearMap{T} end end +FillMap(λ, m::Int, n::Int) = FillMap(λ, (m, n)) + # properties Base.size(A::FillMap) = A.size MulStyle(A::FillMap) = FiveArg() diff --git a/test/fillmap.jl b/test/fillmap.jl index c2e04f84..03ce8d8e 100644 --- a/test/fillmap.jl +++ b/test/fillmap.jl @@ -4,9 +4,9 @@ using LinearMaps, LinearAlgebra, Test M, N = 2, 3 μ = rand() for λ in (true, false, 3, μ, μ + 2im) - L = LinearMap(λ, (M, N)) - @test L == LinearMap(λ, M, N) - @test occursin("$M×$N LinearMaps.FillMap{$(typeof(λ))} with fill value: $λ", sprint((t, s) -> show(t, "text/plain", s), L)) + L = FillMap(λ, (M, N)) + @test L == FillMap(λ, M, N) + @test occursin("$M×$N FillMap{$(typeof(λ))} with fill value: $λ", sprint((t, s) -> show(t, "text/plain", s), L)) @test LinearMaps.MulStyle(L) === LinearMaps.FiveArg() A = fill(λ, (M, N)) x = rand(typeof(λ) <: Real ? Float64 : ComplexF64, 3) @@ -14,8 +14,8 @@ using LinearMaps, LinearAlgebra, Test w = similar(x, 2) W = similar(X, 2, 4) @test size(L) == (M, N) - @test adjoint(L) == LinearMap(adjoint(λ), (3,2)) - @test transpose(L) == LinearMap(λ, (3,2)) + @test adjoint(L) == FillMap(adjoint(λ), (3,2)) + @test transpose(L) == FillMap(λ, (3,2)) @test Matrix(L) == A @test L * x ≈ A * x @test mul!(w, L, x) ≈ A * x @@ -25,16 +25,16 @@ using LinearMaps, LinearAlgebra, Test @test mul!(copy(W), L, X, α, β) ≈ λ * reduce(vcat, sum(X, dims=1) for _ in 1:2) * α + W * β end end - @test issymmetric(LinearMap(μ + 1im, (3, 3))) - @test ishermitian(LinearMap(μ + 0im, (3, 3))) - @test isposdef(LinearMap(μ, (1,1))) == isposdef(μ) - @test !isposdef(LinearMap(μ, (3,3))) + @test issymmetric(FillMap(μ + 1im, (3, 3))) + @test ishermitian(FillMap(μ + 0im, (3, 3))) + @test isposdef(FillMap(μ, (1,1))) == isposdef(μ) + @test !isposdef(FillMap(μ, (3,3))) α = rand() β = rand() - @test LinearMap(μ, (M, N)) + LinearMap(α, (M, N)) == LinearMap(μ + α, (M, N)) - @test LinearMap(μ, (M, N)) - LinearMap(α, (M, N)) == LinearMap(μ - α, (M, N)) - @test α*LinearMap(μ, (M, N)) == LinearMap(α * μ, (M, N)) - @test LinearMap(μ, (M, N))*α == LinearMap(μ * α, (M, N)) - @test LinearMap(μ, (M, N))*LinearMap(μ, (N, M)) == LinearMap(μ^2*N, (M, M)) - @test Matrix(LinearMap(μ, (M, N))*LinearMap(μ, (N, M))) ≈ fill(μ, (M, N))*fill(μ, (N, M)) + @test FillMap(μ, (M, N)) + FillMap(α, (M, N)) == FillMap(μ + α, (M, N)) + @test FillMap(μ, (M, N)) - FillMap(α, (M, N)) == FillMap(μ - α, (M, N)) + @test α*FillMap(μ, (M, N)) == FillMap(α * μ, (M, N)) + @test FillMap(μ, (M, N))*α == FillMap(μ * α, (M, N)) + @test FillMap(μ, (M, N))*FillMap(μ, (N, M)) == FillMap(μ^2*N, (M, M)) + @test Matrix(FillMap(μ, (M, N))*FillMap(μ, (N, M))) ≈ fill(μ, (M, N))*fill(μ, (N, M)) end \ No newline at end of file diff --git a/test/numbertypes.jl b/test/numbertypes.jl index 95ab2940..8ad190ef 100644 --- a/test/numbertypes.jl +++ b/test/numbertypes.jl @@ -45,7 +45,7 @@ using Test, LinearMaps, LinearAlgebra, Quaternions @test M == (L * L * γ) * β == (L * L * α) * β == (L * L * α) * β.λ @test length(M.maps) == 3 @test M.maps[1].λ == γ*β.λ - @test γ*LinearMap(γ, (3, 4)) == LinearMap(γ^2, (3, 4)) == LinearMap(γ, (3, 4))*γ + @test γ*FillMap(γ, (3, 4)) == FillMap(γ^2, (3, 4)) == FillMap(γ, (3, 4))*γ # exercise non-RealOrComplex scalar operations @test Array(γ * (L'*L)) ≈ γ * (A'*A) # CompositeMap From 649ed542fe1d0c2300b45e64e6896cbbdd917b84 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Thu, 10 Dec 2020 11:23:28 +0100 Subject: [PATCH 17/17] typo fix --- docs/src/types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/types.md b/docs/src/types.md index a8faa1d4..fa6b5e8c 100644 --- a/docs/src/types.md +++ b/docs/src/types.md @@ -84,7 +84,7 @@ Base.cat SparseArrays.blockdiag ``` -### `FillMap`` +### `FillMap` Type for lazily representing constantly filled matrices.