Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions src/linalg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -993,27 +993,39 @@ function cond(A::AbstractSparseMatrixCSC, p::Real=2)
# as for a dense matrix; the estimator of the inverse's norm needs at least one column
(p == 1 || p == Inf) && checksquare(A) == 0 && return zero(T)
if p == 1 || p == Inf
# a singular matrix has an infinite condition number, as for a dense matrix; a
# triangular or diagonal `factorize(A)` reports it only in the solve
normAinv = try
p == 1 ? opnormestinv(A) : opnormestinv(copy(A'))
B = p == 1 ? A : copy(A')
# a singular matrix has an infinite condition number, as for a dense matrix
F = try
factorize(B)
catch e
e isa SingularException || rethrow()
return convert(T, Inf)
end
return convert(T, opnorm(A, p) * normAinv)
_issingularfactor(F) && return convert(T, Inf)
return convert(T, opnorm(A, p) * _opnormestinv(B, F, min(2, size(B, 1))))
elseif p == 2
throw(ArgumentError("only 1- and Inf-norm condition numbers are implemented for sparse matrices, for 2-norm try cond(Array(A), 2) instead"))
else
throw(ArgumentError("second argument must be either 1 or Inf, got $p"))
end
end

function opnormestinv(A::AbstractSparseMatrixCSC{T}, t::Integer = min(2,maximum(size(A)))) where T
# `factorize` throws for a singular LU or Hermitian factorization, but leaves a triangular
# or diagonal one to the solve
_issingularfactor(F) = false
_issingularfactor(D::Diagonal) = any(iszero, D.diag)
_issingularfactor(L::LowerTriangular{<:Any,<:AbstractSparseMatrixCSC}) = !iszero(_trisingular(false, parent(L)))
_issingularfactor(U::UpperTriangular{<:Any,<:AbstractSparseMatrixCSC}) = !iszero(_trisingular(true, parent(U)))

function opnormestinv(A::AbstractSparseMatrixCSC, t::Integer = min(2,maximum(size(A))))
checksquare(A)
return _opnormestinv(A, factorize(A), t)
end

# the estimate for the factorization `F` of `A`
function _opnormestinv(A::AbstractSparseMatrixCSC{T}, F, t::Integer) where T
maxiter = 5
# Check the input
n = checksquare(A)
F = factorize(A)
n = size(A, 1)
if t <= 0
throw(ArgumentError("number of blocks must be a positive integer"))
end
Expand Down
5 changes: 4 additions & 1 deletion test/solvers/issues.jl
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,11 @@ end

@testset "cond of a singular matrix is Inf" begin
@test cond(sparse([1.0 2; 3 6]), 1) === Inf
# the triangular solve, not the factorization, finds this one singular
# `factorize` leaves the zero on the diagonal of a triangular or diagonal factor
@test cond(sparse([1.0 0; 2 0]), Inf) === Inf
@test cond(sparse([1.0 0; 0 0]), 1) === Inf
# Hermitian, so `factorize` tries Cholesky and LDLt before the LU finds it singular
@test cond(sparse([1.0 1; 1 1]), 1) === Inf
# the LU works in double precision, but the result is of the matrix's precision
@test cond(sparse(Float32[1 2; 3 4]), 1) isa Float32
end
Expand Down
Loading