-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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 that sparse matrix is valid before constructing a CHOLMOD.Sparse #20464
Conversation
base/sparse/cholmod.jl
Outdated
# check if columns are sorted | ||
# checks | ||
## length of input | ||
if length(nzval) != length(rowval) |
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.
CSC does not require that length(nzval) == length(rowval)
, and we do not generally enforce that condition (nor should we)?
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 can see why you'd like to allow length(nzval) > colptr[end]
but why would you allow length(nzval) != length(rowval)
? It also appears that CHOLMOD assumes that these have the same size.
base/sparse/cholmod.jl
Outdated
if length(nzval) != length(rowval) | ||
throw(ArgumentError("nzval and rowval must have same length")) | ||
end | ||
if length(colptr) != n + 1 |
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 length(colptr) >= n + 1
?
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.
Why shouldn't length(colptr) == n + 1
? Again, that seems to be the assumption of CHOLMOD.
base/sparse/cholmod.jl
Outdated
if length(colptr) != n + 1 | ||
throw(ArgumentError("length of colptr must be n + 1 = $(n + 1) but was $(length(colptr))")) | ||
end | ||
if colptr[end] != length(rowval) |
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.
CSC does not require that length(rowval) == colptr[n + 1]
, and we do not generally enforce that condition (nor should we)? Perhaps length(rowval) + 1 >= colptr[n + 1]
and likewise for nzval
?
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 can see why you don't want this so I'll change it. However, I think I might have assumed this all over the CHOLMOD wrappers so there might be other places where the number of non-zeros is queried with length(nzval)
which is then wrong.
base/sparse/cholmod.jl
Outdated
@@ -898,6 +909,17 @@ function (::Type{Sparse}){Tv<:VTypes}(m::Integer, n::Integer, | |||
end | |||
|
|||
function (::Type{Sparse}){Tv<:VTypes}(A::SparseMatrixCSC{Tv,SuiteSparse_long}, stype::Integer) | |||
## Check length of input. This should never fail but see #20024 | |||
if length(A.nzval) != length(A.rowval) |
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.
base/sparse/cholmod.jl
Outdated
if length(A.colptr) != size(A,2) + 1 | ||
throw(ArgumentError("length of colptr must be size(A,2) + 1 = $(size(A,2) + 1) but was $(length(A.colptr))")) | ||
end | ||
if A.colptr[end] != length(A.rowval) + 1 |
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. discussion in the review comments above, the general argument for the weaker conditions ( Re. Re. Re. no restrictions on the relative lengths of Of course if CHOLMOD expects any of the stronger conditions and will complain or fail if they are not satisfied, I agree wholeheartedly that checking them at CHOLMOD entry points is a great idea :). As you note above some code in Base assumes some subset of the stronger conditions. For reasons such as the above, we might want to ween that code off those assumptions over time, and e.g. remove automatic Best! |
Thanks for the explanation. Generally, I'm fine with what you suggest but we should then really take a pass through the sparse code to make sure we use the buffers accordingly. E.g. I can see that this method makes the same assumptions as I did whereas the one right above doesn't. I'll update this PR to use your CSC definition. |
Wholeheartedly agreed :). Best! |
26e5b8d
to
a26f482
Compare
I've changed the tests following the conclusion above. I've also changed the |
base/sparse/cholmod.jl
Outdated
if length(colptr) <= n | ||
throw(ArgumentError("length of colptr must be at least n + 1 = $(n + 1) but was $(length(colptr))")) | ||
end | ||
if colptr[n + 1] > length(rowval) |
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.
Should these checks be colptr[n + 1] > length(rowval) + 1
and colptr[n + 1] > length(nzval) + 1
? If memory serves, colptr[n + 1]
should be one greater than the number of stored entries? (Or is colptr
indexed from zero here, in which case the existing checks would be correct?)
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.
This constructor is zero based. I'm wondering if it should have a more alarming name for that reason.
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.
This constructor is zero based. I'm wondering if it should have a more alarming name for that reason.
That sounds great! Similarly, if some instances of colptr
are zero based, might be worth similarly naming them appropriately? Best!
base/sparse/cholmod.jl
Outdated
@@ -869,12 +880,12 @@ function (::Type{Sparse}){Tv<:VTypes}(m::Integer, n::Integer, | |||
end | |||
end | |||
|
|||
o = allocate_sparse(m, n, length(nzval), iss, true, stype, Tv) | |||
o = allocate_sparse(m, n, colptr[n + 1], iss, true, stype, Tv) |
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.
Should the third argument to allocate_sparse
be the number of stored entries or one more than the number of stored entries? (Or is colptr
indexed from zero here, in which case the existing statement is correct?)
base/sparse/cholmod.jl
Outdated
unsafe_copy!(s.i, pointer(rowval), length(rowval)) | ||
unsafe_copy!(s.x, pointer(nzval), length(nzval)) | ||
unsafe_copy!(s.p, pointer(colptr), n + 1) | ||
unsafe_copy!(s.i, pointer(rowval), colptr[n + 1]) |
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.
Should these be colptr[n + 1] - 1
, i.e. the number of stored entries? (Or is colptr
indexed from zero here, in which case the existing statement is correct?)
a26f482
to
344605d
Compare
@@ -42,7 +42,7 @@ julia> nnz(A) | |||
3 | |||
``` | |||
""" | |||
nnz(S::SparseMatrixCSC) = Int(S.colptr[end]-1) | |||
nnz(S::SparseMatrixCSC) = Int(S.colptr[S.n + 1]-1) |
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.
the distinction here could use a more direct test that doesn't go through cholmod-specific methods
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've added some tests.
Don't use length of underlying arrays to query sizes Rename variables in zero based constructors
344605d
to
f047e92
Compare
These are just some consistency checks of the inputs before constructing a
CHOLMOD.Sparse
. Julia'sSparseMatrixCSC
should always pass the tests unless the buffers have been directly modified. However, as seen in JuliaLang/LinearAlgebra.jl#394, serialization might corrupt the sparse matrices which can lead to cryptic bugs. The checks introduced in this PR would probably have made it easier to detect the issue.Fixes JuliaLang/LinearAlgebra.jl#394