Skip to content

Aliasing problem with SparseMatrixCSC and SparseVector #34630

@KlausC

Description

@KlausC

In v1.2.0 and v1.5.0 it is easy to create a corrupted sparse object unintentionally:

julia> B = sparse([1 1;0 0]);
julia> A = SparseMatrixCSC{Float64,Int}(B);
julia> A[2,1] = 1
julia> A
2×2 SparseMatrixCSC{Float64,Int64} with 3 stored entries:
  [1, 1]  =  1.0
  [2, 1]  =  1.0
  [1, 2]  =  1.0
julia> B
2×2 SparseMatrixCSC{Int64,Int64} with 3 stored entries:
  [1, 1]  =  1
  [2, 1]  =  1
  [1, 2]  =  #undef

julia> b = sparse([1;0]);
julia> a = SparseVector{Float64,Int}(b);
julia> a[2] = 1;
julia> a
2-element SparseVector{Float64,Int64} with 2 stored entries:
  [1]  =  1.0
  [2]  =  1.0
julia> b
2-element SparseVector{Int64,Int64} with 1 stored entry:
  [1]  =  1
  [2]  =  #undef

Reason seems to be that the constructors SparseMatrixCSC{Tv,Ti}(S::SparseMatrixCSC) and SparseVector{Tv,Ti}(s:SparseVector)
may produce objects, which share some but not all of their fields with the source objects.
That is for example the case, if Ti == eltype(S.rowval) and Tv != eltype(S.nzval). That results in inconsistent objects when one of them is modified. For example:

julia> dump(b)
SparseVector{Int64,Int64}
  n: Int64 2
  nzind: Array{Int64}((2,)) [1, 2]
  nzval: Array{Int64}((1,)) [1]

where the sizes of the component vectors of b diverge.
IMO the constructors should be changed to not use convert, but make copies.

The issue surfaced in v1.5 (but not in v1.2) when doing a calculation like follows, where the
corruption of B was rather unexpected:

julia> B = Symmetric(sparse([1 1;0 0]));
julia> B \ ones(2);
ERROR: MethodError: no method matching lu! ...

julia> B
2×2 Symmetric{Int64,SparseMatrixCSC{Int64,Int64}}:
   1     #undef
 #undef    0

Metadata

Metadata

Assignees

No one assigned

    Labels

    sparseSparse arrays

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions