Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redundant dimension wastes space and allows disagreement #178

Merged
merged 8 commits into from
Oct 4, 2023
Merged
6 changes: 6 additions & 0 deletions src/deprecates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ using Base: @deprecate
@deprecate PDiagMat(v::AbstractVector, inv_v::AbstractVector) PDiagMat(v)

@deprecate dim(a::AbstractMatrix) LinearAlgebra.checksquare(a)

@deprecate PDMat{T,S}(d::Int, m::AbstractMatrix{T}, c::Cholesky{T,S}) where {T,S} PDMat{T,S}(m, c)

@deprecate PDiagMat(dim::Int, diag::AbstractVector{<:Real}) PDiagMat(diag)
@deprecate PDiagMat{T,V}(dim, diag) where {T<:Real, V<:AbstractVector{T}} PDiagMat{T,V}(diag)

9 changes: 7 additions & 2 deletions src/pdiagmat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@
Positive definite diagonal matrix.
"""
struct PDiagMat{T<:Real,V<:AbstractVector{T}} <: AbstractPDMat{T}
dim::Int
diag::V
devmotion marked this conversation as resolved.
Show resolved Hide resolved
end

PDiagMat(v::AbstractVector{<:Real}) = PDiagMat{eltype(v),typeof(v)}(length(v), v)
function Base.getproperty(a::PDiagMat, s::Symbol)
if s === :dim
return length(getfield(a, :diag))
end
return getfield(a, s)
end
Base.propertynames(::PDiagMat) = (:diag, :dim)

AbstractPDMat(A::Diagonal{<:Real}) = PDiagMat(A.diag)
AbstractPDMat(A::Symmetric{<:Real,<:Diagonal{<:Real}}) = PDiagMat(A.data.diag)
Expand Down
18 changes: 13 additions & 5 deletions src/pdmat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,31 @@
Full positive definite matrix together with a Cholesky factorization object.
"""
struct PDMat{T<:Real,S<:AbstractMatrix} <: AbstractPDMat{T}
dim::Int
mat::S
chol::Cholesky{T,S}

PDMat{T,S}(d::Int,m::AbstractMatrix{T},c::Cholesky{T,S}) where {T,S} = new{T,S}(d,m,c)
PDMat{T,S}(m::AbstractMatrix{T},c::Cholesky{T,S}) where {T,S} = new{T,S}(m,c)
end

function PDMat(mat::AbstractMatrix,chol::Cholesky{T,S}) where {T,S}
d = size(mat, 1)
size(chol, 1) == d ||
d = LinearAlgebra.checksquare(mat)
if size(chol, 1) != d
throw(DimensionMismatch("Dimensions of mat and chol are inconsistent."))
PDMat{T,S}(d, convert(S, mat), chol)
end
PDMat{T,S}(convert(S, mat), chol)
end

PDMat(mat::AbstractMatrix) = PDMat(mat, cholesky(mat))
PDMat(fac::Cholesky) = PDMat(AbstractMatrix(fac), fac)

function Base.getproperty(a::PDMat, s::Symbol)
if s === :dim
return size(getfield(a, :mat), 1)
end
return getfield(a, s)
end
Base.propertynames(::PDMat) = (:mat, :chol, :dim)

AbstractPDMat(A::Cholesky) = PDMat(A)

### Conversion
Expand Down
18 changes: 13 additions & 5 deletions src/pdsparsemat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@
Sparse positive definite matrix together with a Cholesky factorization object.
"""
struct PDSparseMat{T<:Real,S<:AbstractSparseMatrix} <: AbstractPDMat{T}
dim::Int
mat::S
chol::CholTypeSparse

PDSparseMat{T,S}(d::Int,m::AbstractSparseMatrix{T},c::CholTypeSparse) where {T,S} =
new{T,S}(d,m,c) #add {T} to CholTypeSparse argument once #14076 is implemented
PDSparseMat{T,S}(m::AbstractSparseMatrix{T},c::CholTypeSparse) where {T,S} =
new{T,S}(m,c) #add {T} to CholTypeSparse argument once #14076 is implemented
end
@deprecate PDSparseMat{T,S}(d::Int, m::AbstractSparseMatrix{T}, c::CholTypeSparse) where {T,S} PDSparseMat{T,S}(m, c)

function PDSparseMat(mat::AbstractSparseMatrix,chol::CholTypeSparse)
d = size(mat, 1)
d = LinearAlgebra.checksquare(mat)
size(chol, 1) == d ||
throw(DimensionMismatch("Dimensions of mat and chol are inconsistent."))
PDSparseMat{eltype(mat),typeof(mat)}(d, mat, chol)
PDSparseMat{eltype(mat),typeof(mat)}(mat, chol)
end

PDSparseMat(mat::SparseMatrixCSC) = PDSparseMat(mat, cholesky(mat))
PDSparseMat(fac::CholTypeSparse) = PDSparseMat(sparse(fac), fac)

function Base.getproperty(a::PDSparseMat, s::Symbol)
if s === :dim
return size(getfield(a, :mat), 1)
end
return getfield(a, s)
end
Base.propertynames(::PDSparseMat) = (:mat, :chol, :dim)

AbstractPDMat(A::SparseMatrixCSC) = PDSparseMat(A)
AbstractPDMat(A::CholTypeSparse) = PDSparseMat(A)

Expand Down
67 changes: 67 additions & 0 deletions test/pdmtypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ using Test
test_pdmat(PDSparseMat(sparse(M)), M, cmat_eq=true, verbose=1, t_eig=false)
end
end

@testset "test deprecated internal constructors" begin
m = Matrix{T}(I, 2, 2)
C = cholesky(m)
@test @test_deprecated(PDMat{T,typeof(m)}(2, m, C)) == PDMat(m)
d = ones(T,2)
@test @test_deprecated(PDiagMat(2, d)) == @test_deprecated(PDiagMat{T,Vector{T}}(2, d)) == PDiagMat(d)
if HAVE_CHOLMOD
s = SparseMatrixCSC{T}(I, 2, 2)
@test @test_deprecated(PDSparseMat{T, typeof(s)}(2, s, cholesky(s))) == PDSparseMat(s)
end
end
end

@testset "zero-dimensional matrices" begin
Expand Down Expand Up @@ -163,4 +175,59 @@ using Test
@test M isa PDSparseMat
@test Matrix(M) ≈ A
end

@testset "properties and fields" begin
for dim in (1, 5, 10)
x = rand(dim, dim)
M = PDMat(x' * x + I)
@test fieldnames(typeof(M)) == (:mat, :chol)
@test propertynames(M) == (fieldnames(typeof(M))..., :dim)
@test getproperty(M, :dim) === dim
for p in fieldnames(typeof(M))
@test getproperty(M, p) === getfield(M, p)
end

M = PDiagMat(rand(dim))
@test fieldnames(typeof(M)) == (:diag,)
@test propertynames(M) == (fieldnames(typeof(M))..., :dim)
@test getproperty(M, :dim) === dim
for p in fieldnames(typeof(M))
@test getproperty(M, p) === getfield(M, p)
end

M = ScalMat(dim, rand())
@test fieldnames(typeof(M)) == (:dim, :value)
@test propertynames(M) == fieldnames(typeof(M))
for p in fieldnames(typeof(M))
@test getproperty(M, p) === getfield(M, p)
end

if HAVE_CHOLMOD
x = sprand(dim, dim, 0.2)
M = PDSparseMat(x' * x + I)
@test fieldnames(typeof(M)) == (:mat, :chol)
@test propertynames(M) == (fieldnames(typeof(M))..., :dim)
@test getproperty(M, :dim) === dim
for p in fieldnames(typeof(M))
@test getproperty(M, p) === getfield(M, p)
end
end
end
end

@testset "Incorrect dimensions" begin
x = rand(10, 10)
A = x * x' + I
C = cholesky(A)
@test_throws DimensionMismatch PDMat(A[:, 1:(end - 1)], C)
@test_throws DimensionMismatch PDMat(A[1:(end - 1), 1:(end - 1)], C)

if HAVE_CHOLMOD
x = sprand(10, 10, 0.2)
A = x * x' + I
C = cholesky(A)
@test_throws DimensionMismatch PDSparseMat(A[:, 1:(end - 1)], C)
@test_throws DimensionMismatch PDSparseMat(A[1:(end - 1), 1:(end - 1)], C)
end
end
end
Loading