-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
check kwarg for factorizations #27336
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
check kwarg for factorizations #27336
Conversation
db3ed25 to
b8aac1f
Compare
andreasnoack
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Only thing I'm wondering is if throw=true is better than check=false.
|
Stefan pointed out (and I agree) that Also, should we do the same for |
|
I don't have strong opinions about the name so let's just stick with It would be great to handle this consistently for the sparse versions as well. For LU, I guess it is a matter of storing the julia/stdlib/SuiteSparse/src/umfpack.jl Lines 230 to 237 in 22590d5
|
|
So to use UMFPACK, we need to first precompute a symbolic factorization and then do the numeric thing. Is it possible for the |
|
I think the singular warning is only returned in the numerical factorization. The symbolic factorization doesn't fail on structural singularity. Instead of storing the |
That means we have to propagate the |
I think you'd only have to add a (positional?) argument to |
1e57e83 to
e970dd6
Compare
|
(Apologies for review latency; I hope to eke out review time this weekend! :) ) |
| rook pivoting is not used. | ||
| When `check = true` the matrix is checked for singularity, and makes sure | ||
| the factorization is successful, or else throw an error. When `check = false` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps slightly more compact "When check = true, an error is thrown if the decomposition fails. When check = false, responsibility for checking the decomposition's validity (via issuccess) lies with the user." ?
| end | ||
| check && checkpositivedefinite(info) | ||
| return C | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps slightly simpler?
function cholesky!(A::RealHermSymComplexHerm, ::Val{false}=Val(false); check::Bool = true)
C, info = _chol!(A.data, A.uplo == 'U' ? UpperTriangular : LowerTriangular)
check && checkpositivedefinite(info)
return Cholesky(C.data, A.uplo, info)
end
stdlib/LinearAlgebra/src/cholesky.jl
Outdated
| When `check = true` the matrix will be checked for positive definiteness, and make sure | ||
| the factorization was successful, or else throw an error. When `check = false` | ||
| the user is responsible to make sure the result is valid, with | ||
| [`issuccess`](@ref), before passing the result to downstream functions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ref. comment on similar snippet above :).
| inv(C::Cholesky{<:BlasFloat,<:StridedMatrix}) = inv!(copy(C)) | ||
|
|
||
| function inv(C::CholeskyPivoted) | ||
| chkfullrank(C) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like we can afford the vowel in check 😄. Perhaps some day...
| :($(esc(info)) == 0 ? $(esc(A)) : throw(SingularException($(esc(info))))) | ||
| end | ||
| checkpositivedefinite(info) = info == 0 || throw(PosDefException(info)) | ||
| checknonsingular(info) = info == 0 || throw(SingularException(info)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| return convert(S, det(UpperTriangular(A))) | ||
| end | ||
| return det(lu(A)) | ||
| return det(lu(A; check = false)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this check = false belong here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, otherwise we throw, and det deals with failed factorizations:
julia/stdlib/LinearAlgebra/src/lu.jl
Line 333 in c8ce43a
| issuccess(F) || return zero(T) |
stdlib/LinearAlgebra/src/lu.jl
Outdated
| AA = similar(A, S) | ||
| copyto!(AA, A) | ||
| F = lu!(AA, Val(false)) | ||
| F = lu!(AA, Val(false); check = false) # ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re. the # ?, looks sound in combination with the downstream logic.
| end | ||
| F = bunchkaufman(R; check = false) | ||
| @test sprint(show, "text/plain", F) == "Failed factorization of type $(typeof(F))" | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these tests preserve all that can/should be preserved from the block removed above? If so, carry on :). (Edit: E.g. eltype combinations.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, should hit all relevant code paths.
stdlib/SuiteSparse/src/cholmod.jl
Outdated
| Hermitian{T,SparseMatrixCSC{T,SuiteSparse_long}}}; | ||
| shift = 0.0) where {T<:Real} = | ||
| cholesky!(F, Sparse(A); shift = shift) | ||
| shift = 0.0, check::Bool = true) where {T<:Real} = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though the indentation was off before, perhaps indent this part of the signature to match the preceding part?
stdlib/SuiteSparse/src/umfpack.jl
Outdated
| function LinearAlgebra.issuccess(lu::UmfpackLU) | ||
| U, status = umfpack_numeric!(lu, false) | ||
| return status == UMFPACK_OK | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't worked through the umfpack_numeric! logic, but presume that calling this issuccess method on a completed UmfpackLU will not result in an additional numerical decomposition phase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering the same when I wrote this, my tests suggested it was an quick return, but I tried again now (with larger matrices) and that does not seem to be the case... Might have to store status in the struct after all!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Browsed through the source and indeed it doesn't look like it is returning early if the factorization is already computed so storing the status is probably the way to go. UMFPACK stores an estimate of the reciprocal condition number and this number is used to determine singularity but I don't think we have a way to extract that estimate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done; I think the diff is smaller for this implementation as well.
Sacha0
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Wonderful to cleanup to the use-sites :). Much thanks @fredrikekre!
Caveat: I did not examine the logic for the sparse decompositions carefully.
30b7db2 to
112731f
Compare
112731f to
6238946
Compare
6238946 to
fa6f586
Compare
|
FreeBSD failure is OOM; Windows failures are timeouts. Do we have reason to be suspicious of this PR on non-Linux platforms? It does pass on macOS at least, so it's fine on at least two different OSes. |
|
It passed on FreeBSD before the rebase as well. |
|
Ok, good to go then? |
* check = (true|false) for LinearAlgebra.cholesky[!] * check = (true|false) for LinearAlgebra.lu[!] * check = (true|false) for LinearAlgebra.bunchkaufman[!] * check = (true|false) for CHOLMOD.cholesky[!] and CHOLMOD.ldlt[!] * check = (true|false) for UMFPACK.lu
Still some cleanup to do.