Skip to content

Commit

Permalink
move sparsarrays to stdlib
Browse files Browse the repository at this point in the history
  • Loading branch information
fredrikekre committed Jan 8, 2018
1 parent 16707ba commit 77edba7
Show file tree
Hide file tree
Showing 53 changed files with 804 additions and 769 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,8 @@ Deprecated or removed
* The functions `eigs` and `svds` have been moved to the `IterativeEigensolvers` standard
library module ([#24714]).

* Sparse array functionality has moved to the `SparseArrays` standard library ([#TBD]).

* `@printf` and `@sprintf` have been moved to the `Printf` standard library ([#23929],[#25056]).

* `isnumber` has been deprecated in favor of `isnumeric`, `is_assigned_char`
Expand Down
8 changes: 4 additions & 4 deletions base/asyncmap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,10 @@ end

# TODO: Optimize for sparse arrays
# For now process as regular arrays and convert back
function asyncmap(f, s::AbstractSparseArray...; kwargs...)
sa = map(Array, s)
return sparse(asyncmap(f, sa...; kwargs...))
end
# function asyncmap(f, s::AbstractSparseArray...; kwargs...)
# sa = map(Array, s)
# return sparse(asyncmap(f, sa...; kwargs...))
# end

mutable struct AsyncCollector
f
Expand Down
679 changes: 340 additions & 339 deletions base/deprecated.jl

Large diffs are not rendered by default.

27 changes: 1 addition & 26 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -434,15 +434,13 @@ export
minimum,
minmax,
ndims,
nonzeros,
ones,
parent,
parentindices,
partialsort,
partialsort!,
partialsortperm,
partialsortperm!,
permute,
permute!,
permutedims,
permutedims!,
Expand Down Expand Up @@ -513,7 +511,6 @@ export
# linear algebra
bkfact!,
bkfact,
blkdiag,
chol,
cholfact!,
cholfact,
Expand Down Expand Up @@ -594,10 +591,6 @@ export
,
×,

# sparse
dropzeros,
dropzeros!,

# bitarrays
falses,
flipbits!,
Expand Down Expand Up @@ -1183,22 +1176,4 @@ export
@goto,
@view,
@views,
@static,

# SparseArrays module re-exports
SparseArrays,
AbstractSparseArray,
AbstractSparseMatrix,
AbstractSparseVector,
SparseMatrixCSC,
SparseVector,
issparse,
sparse,
sparsevec,
spdiagm,
sprand,
sprandn,
spzeros,
rowvals,
nzrange,
nnz
@static
3 changes: 2 additions & 1 deletion base/linalg/bidiag.jl
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ broadcast(::typeof(big), B::Bidiagonal) = Bidiagonal(big.(B.dv), big.(B.ev), B.u
# On the other hand, similar(B, [neweltype,] shape...) should yield a sparse matrix.
# The first method below effects the former, and the second the latter.
similar(B::Bidiagonal, ::Type{T}) where {T} = Bidiagonal(similar(B.dv, T), similar(B.ev, T), B.uplo)
similar(B::Bidiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...)
# The method below is moved to SparseArrays for now
# similar(B::Bidiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...)


###################
Expand Down
2 changes: 0 additions & 2 deletions base/linalg/dense.jl
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,6 @@ Vector `kv.second` will be placed on the `kv.first` diagonal.
versions with fast arithmetic, see [`Diagonal`](@ref), [`Bidiagonal`](@ref)
[`Tridiagonal`](@ref) and [`SymTridiagonal`](@ref).
See also: [`spdiagm`](@ref)
# Examples
```jldoctest
julia> diagm(1 => [1,2,3])
Expand Down
3 changes: 2 additions & 1 deletion base/linalg/diagonal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ Array(D::Diagonal) = Matrix(D)
# On the other hand, similar(D, [neweltype,] shape...) should yield a sparse matrix.
# The first method below effects the former, and the second the latter.
similar(D::Diagonal, ::Type{T}) where {T} = Diagonal(similar(D.diag, T))
similar(D::Diagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...)
# The method below is moved to SparseArrays for now
# similar(D::Diagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...)

copyto!(D1::Diagonal, D2::Diagonal) = (copyto!(D1.diag, D2.diag); D1)

Expand Down
6 changes: 4 additions & 2 deletions base/linalg/tridiag.jl
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ end
# On the other hand, similar(S, [neweltype,] shape...) should yield a sparse matrix.
# The first method below effects the former, and the second the latter.
similar(S::SymTridiagonal, ::Type{T}) where {T} = SymTridiagonal(similar(S.dv, T), similar(S.ev, T))
similar(S::SymTridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...)
# The method below is moved to SparseArrays for now
# similar(S::SymTridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...)

#Elementary operations
broadcast(::typeof(abs), M::SymTridiagonal) = SymTridiagonal(abs.(M.dv), abs.(M.ev))
Expand Down Expand Up @@ -494,7 +495,8 @@ Array(M::Tridiagonal) = Matrix(M)
# On the other hand, similar(M, [neweltype,] shape...) should yield a sparse matrix.
# The first method below effects the former, and the second the latter.
similar(M::Tridiagonal, ::Type{T}) where {T} = Tridiagonal(similar(M.dl, T), similar(M.d, T), similar(M.du, T))
similar(M::Tridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...)
# The method below is moved to SparseArrays for now
# similar(M::Tridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...)

# Operations on Tridiagonal matrices
copyto!(dest::Tridiagonal, src::Tridiagonal) = (copyto!(dest.dl, src.dl); copyto!(dest.d, src.d); copyto!(dest.du, src.du); dest)
Expand Down
5 changes: 3 additions & 2 deletions base/sysimg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,8 @@ include("libgit2/libgit2.jl")
include("pkg/pkg.jl")

# sparse matrices, vectors, and sparse linear algebra
include("sparse/sparse.jl")
using .SparseArrays
# include("sparse/sparse.jl")
# using .SparseArrays

include("asyncmap.jl")

Expand Down Expand Up @@ -499,6 +499,7 @@ Base.require(:IterativeEigensolvers)
Base.require(:Mmap)
Base.require(:Profile)
Base.require(:SharedArrays)
Base.require(:SparseArrays)
Base.require(:SuiteSparse)
Base.require(:Test)
Base.require(:Unicode)
Expand Down
8 changes: 6 additions & 2 deletions doc/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ if Sys.iswindows()
cp_q("../stdlib/FileWatching/docs/src/index.md", "src/stdlib/filewatching.md")
cp_q("../stdlib/CRC32c/docs/src/index.md", "src/stdlib/crc32c.md")
cp_q("../stdlib/Dates/docs/src/index.md", "src/stdlib/dates.md")
cp_q("../stdlib/SparseArrays/docs/src/index.md", "src/stdlib/sparsearrays.md")
cp_q("../stdlib/IterativeEigensolvers/docs/src/index.md", "src/stdlib/iterativeeigensolvers.md")
cp_q("../stdlib/Unicode/docs/src/index.md", "src/stdlib/unicode.md")
cp_q("../stdlib/Distributed/docs/src/index.md", "src/stdlib/distributed.md")
Expand All @@ -44,6 +45,7 @@ else
symlink_q("../../../stdlib/FileWatching/docs/src/index.md", "src/stdlib/filewatching.md")
symlink_q("../../../stdlib/CRC32c/docs/src/index.md", "src/stdlib/crc32c.md")
symlink_q("../../../stdlib/Dates/docs/src/index.md", "src/stdlib/dates.md")
symlink_q("../../../stdlib/SparseArrays/docs/src/index.md", "src/stdlib/sparsearrays.md")
symlink_q("../../../stdlib/IterativeEigensolvers/docs/src/index.md", "src/stdlib/iterativeeigensolvers.md")
symlink_q("../../../stdlib/Unicode/docs/src/index.md", "src/stdlib/unicode.md")
symlink_q("../../../stdlib/Distributed/docs/src/index.md", "src/stdlib/distributed.md")
Expand Down Expand Up @@ -125,6 +127,7 @@ const PAGES = [
"stdlib/sharedarrays.md",
"stdlib/filewatching.md",
"stdlib/crc32c.md",
"stdlib/sparsearrays.md",
"stdlib/iterativeeigensolvers.md",
"stdlib/unicode.md",
"stdlib/printf.md",
Expand Down Expand Up @@ -163,12 +166,13 @@ const PAGES = [
]

using DelimitedFiles, Test, Mmap, SharedArrays, Profile, Base64, FileWatching, CRC32c,
Dates, IterativeEigensolvers, Unicode, Distributed, Printf
Dates, SparseArrays, IterativeEigensolvers, Unicode, Distributed, Printf

makedocs(
build = joinpath(pwd(), "_build/html/en"),
modules = [Base, Core, BuildSysImg, DelimitedFiles, Test, Mmap, SharedArrays, Profile,
Base64, FileWatching, Dates, IterativeEigensolvers, Unicode, Distributed, Printf],
Base64, FileWatching, Dates, SparseArrays, IterativeEigensolvers, Unicode,
Distributed, Printf],
clean = false,
doctest = "doctest" in ARGS,
linkcheck = "linkcheck" in ARGS,
Expand Down
1 change: 1 addition & 0 deletions doc/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
* [Memory-mapped I/O](@ref)
* [Base64](@ref)
* [File Events](@ref lib-filewatching)
* [Sparse Arrays](@ref)
* [Iterative Eigensolvers](@ref lib-itereigen)
* [Unicode](@ref)
* [Printf](@ref)
Expand Down
192 changes: 0 additions & 192 deletions doc/src/manual/arrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -762,195 +762,3 @@ julia> r
-1.58553 -0.921517
0.0 0.866567
```

## Sparse Vectors and Matrices

Julia has built-in support for sparse vectors and
[sparse matrices](https://en.wikipedia.org/wiki/Sparse_matrix). Sparse arrays are arrays
that contain enough zeros that storing them in a special data structure leads to savings
in space and execution time, compared to dense arrays.

### [Compressed Sparse Column (CSC) Sparse Matrix Storage](@id man-csc)

In Julia, sparse matrices are stored in the [Compressed Sparse Column (CSC) format](https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_column_.28CSC_or_CCS.29).
Julia sparse matrices have the type [`SparseMatrixCSC{Tv,Ti}`](@ref), where `Tv` is the
type of the stored values, and `Ti` is the integer type for storing column pointers and
row indices. The internal representation of `SparseMatrixCSC` is as follows:

```julia
struct SparseMatrixCSC{Tv,Ti<:Integer} <: AbstractSparseMatrix{Tv,Ti}
m::Int # Number of rows
n::Int # Number of columns
colptr::Vector{Ti} # Column i is in colptr[i]:(colptr[i+1]-1)
rowval::Vector{Ti} # Row indices of stored values
nzval::Vector{Tv} # Stored values, typically nonzeros
end
```

The compressed sparse column storage makes it easy and quick to access the elements in the column
of a sparse matrix, whereas accessing the sparse matrix by rows is considerably slower. Operations
such as insertion of previously unstored entries one at a time in the CSC structure tend to be slow. This is
because all elements of the sparse matrix that are beyond the point of insertion have to be moved
one place over.

All operations on sparse matrices are carefully implemented to exploit the CSC data structure
for performance, and to avoid expensive operations.

If you have data in CSC format from a different application or library, and wish to import it
in Julia, make sure that you use 1-based indexing. The row indices in every column need to be
sorted. If your `SparseMatrixCSC` object contains unsorted row indices, one quick way to sort
them is by doing a double transpose.

In some applications, it is convenient to store explicit zero values in a `SparseMatrixCSC`. These
*are* accepted by functions in `Base` (but there is no guarantee that they will be preserved in
mutating operations). Such explicitly stored zeros are treated as structural nonzeros by many
routines. The [`nnz`](@ref) function returns the number of elements explicitly stored in the
sparse data structure, including structural nonzeros. In order to count the exact number of
numerical nonzeros, use [`count(!iszero, x)`](@ref), which inspects every stored element of a sparse
matrix. [`dropzeros`](@ref), and the in-place [`dropzeros!`](@ref), can be used to
remove stored zeros from the sparse matrix.

```jldoctest
julia> A = sparse([1, 2, 3], [1, 2, 3], [0, 2, 0])
3×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries:
[1, 1] = 0
[2, 2] = 2
[3, 3] = 0
julia> dropzeros(A)
3×3 SparseMatrixCSC{Int64,Int64} with 1 stored entry:
[2, 2] = 2
```

### Sparse Vector Storage

Sparse vectors are stored in a close analog to compressed sparse column format for sparse
matrices. In Julia, sparse vectors have the type [`SparseVector{Tv,Ti}`](@ref) where `Tv`
is the type of the stored values and `Ti` the integer type for the indices. The internal
representation is as follows:

```julia
struct SparseVector{Tv,Ti<:Integer} <: AbstractSparseVector{Tv,Ti}
n::Int # Length of the sparse vector
nzind::Vector{Ti} # Indices of stored values
nzval::Vector{Tv} # Stored values, typically nonzeros
end
```

As for [`SparseMatrixCSC`](@ref), the `SparseVector` type can also contain explicitly
stored zeros. (See [Sparse Matrix Storage](@ref man-csc).).

### Sparse Vector and Matrix Constructors

The simplest way to create a sparse array is to use a function equivalent to the [`zeros`](@ref)
function that Julia provides for working with dense arrays. To produce a
sparse array instead, you can use the same name with an `sp` prefix:

```jldoctest
julia> spzeros(3)
3-element SparseVector{Float64,Int64} with 0 stored entries
```

The [`sparse`](@ref) function is often a handy way to construct sparse arrays. For
example, to construct a sparse matrix we can input a vector `I` of row indices, a vector
`J` of column indices, and a vector `V` of stored values (this is also known as the
[COO (coordinate) format](https://en.wikipedia.org/wiki/Sparse_matrix#Coordinate_list_.28COO.29)).
`sparse(I,J,V)` then constructs a sparse matrix such that `S[I[k], J[k]] = V[k]`. The
equivalent sparse vector constructor is [`sparsevec`](@ref), which takes the (row) index
vector `I` and the vector `V` with the stored values and constructs a sparse vector `R`
such that `R[I[k]] = V[k]`.

```jldoctest sparse_function
julia> I = [1, 4, 3, 5]; J = [4, 7, 18, 9]; V = [1, 2, -5, 3];
julia> S = sparse(I,J,V)
5×18 SparseMatrixCSC{Int64,Int64} with 4 stored entries:
[1 , 4] = 1
[4 , 7] = 2
[5 , 9] = 3
[3 , 18] = -5
julia> R = sparsevec(I,V)
5-element SparseVector{Int64,Int64} with 4 stored entries:
[1] = 1
[3] = -5
[4] = 2
[5] = 3
```

The inverse of the [`sparse`](@ref) and [`sparsevec`](@ref) functions is
[`findnz`](@ref), which retrieves the inputs used to create the sparse array.
There is also a [`findn`](@ref) function which only returns the index vectors.

```jldoctest sparse_function
julia> findnz(S)
([1, 4, 5, 3], [4, 7, 9, 18], [1, 2, 3, -5])
julia> findn(S)
([1, 4, 5, 3], [4, 7, 9, 18])
julia> findnz(R)
([1, 3, 4, 5], [1, -5, 2, 3])
julia> find(!iszero, R)
4-element Array{Int64,1}:
1
3
4
5
```

Another way to create a sparse array is to convert a dense array into a sparse array using
the [`sparse`](@ref) function:

```jldoctest
julia> sparse(Matrix(1.0I, 5, 5))
5×5 SparseMatrixCSC{Float64,Int64} with 5 stored entries:
[1, 1] = 1.0
[2, 2] = 1.0
[3, 3] = 1.0
[4, 4] = 1.0
[5, 5] = 1.0
julia> sparse([1.0, 0.0, 1.0])
3-element SparseVector{Float64,Int64} with 2 stored entries:
[1] = 1.0
[3] = 1.0
```

You can go in the other direction using the [`Array`](@ref) constructor. The [`issparse`](@ref)
function can be used to query if a matrix is sparse.

```jldoctest
julia> issparse(spzeros(5))
true
```

### Sparse matrix operations

Arithmetic operations on sparse matrices also work as they do on dense matrices. Indexing of,
assignment into, and concatenation of sparse matrices work in the same way as dense matrices.
Indexing operations, especially assignment, are expensive, when carried out one element at a time.
In many cases it may be better to convert the sparse matrix into `(I,J,V)` format using [`findnz`](@ref),
manipulate the values or the structure in the dense vectors `(I,J,V)`, and then reconstruct
the sparse matrix.

### Correspondence of dense and sparse methods

The following table gives a correspondence between built-in methods on sparse matrices and their
corresponding methods on dense matrix types. In general, methods that generate sparse matrices
differ from their dense counterparts in that the resulting matrix follows the same sparsity pattern
as a given sparse matrix `S`, or that the resulting sparse matrix has density `d`, i.e. each matrix
element has a probability `d` of being non-zero.

Details can be found in the [Sparse Vectors and Matrices](@ref stdlib-sparse-arrays)
section of the standard library reference.

| Sparse | Dense | Description |
|:-------------------------- |:---------------------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`spzeros(m,n)`](@ref) | [`zeros(m,n)`](@ref) | Creates a *m*-by-*n* matrix of zeros. ([`spzeros(m,n)`](@ref) is empty.) |
| [`sparse(I, n, n)`](@ref) | [`Matrix(I,n,n)`](@ref)| Creates a *n*-by-*n* identity matrix. |
| [`Array(S)`](@ref) | [`sparse(A)`](@ref) | Interconverts between dense and sparse formats. |
| [`sprand(m,n,d)`](@ref) | [`rand(m,n)`](@ref) | Creates a *m*-by-*n* random matrix (of density *d*) with iid non-zero elements distributed uniformly on the half-open interval ``[0, 1)``. |
| [`sprandn(m,n,d)`](@ref) | [`randn(m,n)`](@ref) | Creates a *m*-by-*n* random matrix (of density *d*) with iid non-zero elements distributed according to the standard normal (Gaussian) distribution. |
| [`sprandn(m,n,d,X)`](@ref) | [`randn(m,n,X)`](@ref) | Creates a *m*-by-*n* random matrix (of density *d*) with iid non-zero elements distributed according to the *X* distribution. (Requires the `Distributions` package.) |
Loading

0 comments on commit 77edba7

Please sign in to comment.