Skip to content

libSparse direct solving methods and matrix arithmetic #79

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

Closed
Closed
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
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ version = "0.4.1"
[deps]
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
julia = "1.9"
Expand Down
5 changes: 5 additions & 0 deletions src/AppleAccelerate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ if Sys.isapple()
include("Util.jl")
include("Array.jl")
include("DSP.jl")
include("libSparse/wrappers.jl")
include("libSparse/AASparseMatrices.jl")
include("libSparse/AAFactorizations.jl")
export AASparseMatrix, muladd!
export AAFactorization, solve, solve!, factor!
end

end # module
89 changes: 89 additions & 0 deletions src/libSparse/AAFactorizations.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
@doc """Factorization object.

Create via `f = AAFactorization(A::SparseMatrixCSC{T, Int64})`. Calls to `solve`,
`ldiv`, and their in-place versions require explicitly passing in the
factorization object as the first argument. On construction, the struct stores a
placeholder yet-to-be-factored object: the factorization is computed upon the first call
to `solve`, or by explicitly calling `factor!`. If the matrix is symmetric, it defaults to
a Cholesky factorization; otherwise, it defaults to QR.
"""
mutable struct AAFactorization{T<:vTypes} <: LinearAlgebra.Factorization{T}
matrixObj::AASparseMatrix{T}
_factorization::SparseOpaqueFactorization{T}
end

# returns an AAFactorization containing A and a dummy "yet-to-be-factored" factorization.
function AAFactorization(A::AASparseMatrix{T}) where T<:vTypes
obj = AAFactorization(A, SparseOpaqueFactorization(T))
function cleanup(aa_fact)

Check warning on line 18 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L16-L18

Added lines #L16 - L18 were not covered by tests
# If it's yet-to-be-factored, then there's nothing to release
if !(aa_fact._factorization.status in (SparseYetToBeFactored,

Check warning on line 20 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L20

Added line #L20 was not covered by tests
SparseStatusReleased))
SparseCleanup(aa_fact._factorization)

Check warning on line 22 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L22

Added line #L22 was not covered by tests
end
end
return finalizer(cleanup, obj)

Check warning on line 25 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L25

Added line #L25 was not covered by tests
end

LinearAlgebra.factorize(A::AASparseMatrix{T}) where T<:vTypes = AAFactorization(A)

Check warning on line 28 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L28

Added line #L28 was not covered by tests

# julia's LinearAlgebra module doesn't provide similar constructors.
AAFactorization(M::SparseMatrixCSC{T, Int64}) where T<:vTypes =

Check warning on line 31 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L31

Added line #L31 was not covered by tests
AAFactorization(AASparseMatrix(M))

# easiest way to make this follow the defaults and naming conventions of LinearAlgebra?
# TODO: add tests for the different kinds of factorizations, beyond QR.
function factor!(aa_fact::AAFactorization{T},

Check warning on line 36 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L36

Added line #L36 was not covered by tests
kind::SparseFactorization_t = SparseFactorizationTBD) where T<:vTypes
if aa_fact._factorization.status == SparseYetToBeFactored
if kind == SparseFactorizationTBD

Check warning on line 39 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L38-L39

Added lines #L38 - L39 were not covered by tests
# so far I'm only dealing with ordinary and symmetric
kind = issymmetric(aa_fact.matrixObj) ? SparseFactorizationCholesky :

Check warning on line 41 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L41

Added line #L41 was not covered by tests
SparseFactorizationQR
end
aa_fact._factorization = SparseFactor(kind, aa_fact.matrixObj.matrix)
if aa_fact._factorization.status == SparseMatrixIsSingular

Check warning on line 45 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L44-L45

Added lines #L44 - L45 were not covered by tests
# throw a SingularException? Factoring a singular matrix usually does not
# make it this far: the call to SparseFactor throws an error.
throw(ErrorException("The matrix is singular."))
elseif aa_fact._factorization.status == SparseStatusFailed
throw(ErrorException("Factorization failed: check that the matrix"

Check warning on line 50 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L48-L50

Added lines #L48 - L50 were not covered by tests
* " has the correct properties for the factorization."))
elseif aa_fact._factorization.status != SparseStatusOk
throw(ErrorException("Something went wrong internally. Error type: "

Check warning on line 53 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L52-L53

Added lines #L52 - L53 were not covered by tests
* String(aa_fact._factorization.status)))
end
end
end

function solve(aa_fact::AAFactorization{T}, b::StridedVecOrMat{T}) where T<:vTypes
@assert size(aa_fact.matrixObj)[2] == size(b, 1)
factor!(aa_fact)
x = Array{T}(undef, size(aa_fact.matrixObj)[2], size(b)[2:end]...)
SparseSolve(aa_fact._factorization, b, x)
return x

Check warning on line 64 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L59-L64

Added lines #L59 - L64 were not covered by tests
end

function solve!(aa_fact::AAFactorization{T}, xb::StridedVecOrMat{T}) where T<:vTypes
@assert (xb isa StridedVector) ||

Check warning on line 68 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L67-L68

Added lines #L67 - L68 were not covered by tests
(size(aa_fact.matrixObj)[1] == size(aa_fact.matrixObj)[2]) "Can't in-place " *
"solve: x and b are different sizes and Julia cannot resize a matrix."
factor!(aa_fact)
SparseSolve(aa_fact._factorization, xb)
return xb # written in imitation of KLU.jl, which also returns

Check warning on line 73 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L71-L73

Added lines #L71 - L73 were not covered by tests
end

LinearAlgebra.ldiv!(aa_fact::AAFactorization{T}, xb::StridedVecOrMat{T}) where T<:vTypes =

Check warning on line 76 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L76

Added line #L76 was not covered by tests
solve!(aa_fact, xb)

function LinearAlgebra.ldiv!(x::StridedVecOrMat{T},

Check warning on line 79 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L79

Added line #L79 was not covered by tests
aa_fact::AAFactorization{T},
b::StridedVecOrMat{T}) where T<:vTypes
@assert size(aa_fact.matrixObj)[2] == size(b, 1)
factor!(aa_fact)
SparseSolve(aa_fact._factorization, b, x)
return x

Check warning on line 85 in src/libSparse/AAFactorizations.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AAFactorizations.jl#L82-L85

Added lines #L82 - L85 were not covered by tests
end



116 changes: 116 additions & 0 deletions src/libSparse/AASparseMatrices.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@

@doc """Matrix wrapper, containing the Apple sparse matrix struct
and the pointed-to data. Construct from a `SparseMatrixCSC`.

Multiplication (`*`) and multiply-add (`muladd!`) with both
`Vector` and `Matrix` objects are working.
`transpose` creates a new matrix structure with the opposite
transpose flag, that references the same CSC data.
"""
mutable struct AASparseMatrix{T<:vTypes} <: AbstractMatrix{T}
matrix::SparseMatrix{T}
_colptr::Vector{Clong}
_rowval::Vector{Cint}
_nzval::Vector{T}
end

# I use StridedVector here because it allows for views/references,
# so you can do shallow copies: same pointed-to data. Better way?
"""Constructor for advanced usage: col and row here are 0-indexed CSC data.
Could allow for shared `_colptr`, `_rowval`, `_nzval` between multiple
structs via views or references. Currently unused."""
function AASparseMatrix(n::Int, m::Int,

Check warning on line 22 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L22

Added line #L22 was not covered by tests
col::StridedVector{Clong}, row::StridedVector{Cint},
data::StridedVector{T},
attributes::att_type = ATT_ORDINARY) where T<:vTypes
@assert stride(col, 1) == 1 && stride(row, 1) == 1 && stride(data, 1) == 1

Check warning on line 26 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L26

Added line #L26 was not covered by tests
# I'm assuming here that pointer(row) == pointer(_row_inds),
# ie that col, row, and data are passed by reference, not by value.
s = SparseMatrixStructure(n, m, pointer(col),

Check warning on line 29 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L29

Added line #L29 was not covered by tests
pointer(row), attributes, 1)
m = SparseMatrix(s, pointer(data))
return AASparseMatrix(m, col, row, data)

Check warning on line 32 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L31-L32

Added lines #L31 - L32 were not covered by tests
end

function AASparseMatrix(sparseM::SparseMatrixCSC{T, Int64},

Check warning on line 35 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L35

Added line #L35 was not covered by tests
attributes::att_type = ATT_ORDINARY) where T<:vTypes
if issymmetric(sparseM) && attributes == ATT_ORDINARY
return AASparseMatrix(tril(sparseM), ATT_SYMMETRIC | ATT_LOWER_TRIANGLE)
elseif (istril(sparseM) || istriu(sparseM)) && attributes == ATT_ORDINARY
attributes = istril(sparseM) ? ATT_TRI_LOWER : ATT_TRI_UPPER

Check warning on line 40 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L37-L40

Added lines #L37 - L40 were not covered by tests
end
if attributes in (ATT_TRI_LOWER, ATT_TRI_UPPER) &&

Check warning on line 42 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L42

Added line #L42 was not covered by tests
all(diag(sparseM) .== one(eltype(sparseM)))
attributes |= ATT_UNIT_TRIANGULAR

Check warning on line 44 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L44

Added line #L44 was not covered by tests
end
c = Clong.(sparseM.colptr .+ -1)
r = Cint.(sparseM.rowval .+ -1)
vals = copy(sparseM.nzval)
return AASparseMatrix(size(sparseM)..., c, r, vals, attributes)

Check warning on line 49 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L46-L49

Added lines #L46 - L49 were not covered by tests
end

Base.size(M::AASparseMatrix) = (M.matrix.structure.rowCount,

Check warning on line 52 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L52

Added line #L52 was not covered by tests
M.matrix.structure.columnCount)
Base.eltype(M::AASparseMatrix) = eltype(M._nzval)
LinearAlgebra.issymmetric(M::AASparseMatrix) = (M.matrix.structure.attributes &

Check warning on line 55 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L54-L55

Added lines #L54 - L55 were not covered by tests
ATT_KIND_MASK) == ATT_SYMMETRIC
istri(M::AASparseMatrix) = (M.matrix.structure.attributes

Check warning on line 57 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L57

Added line #L57 was not covered by tests
& ATT_KIND_MASK) == ATT_TRIANGULAR
LinearAlgebra.istriu(M::AASparseMatrix) = istri(M) && (MM.matrix.structure.attributes

Check warning on line 59 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L59

Added line #L59 was not covered by tests
& ATT_TRIANGLE_MASK == ATT_LOWER_TRIANGLE)
LinearAlgebra.istril(M::AASparseMatrix) = istri(M) && (MM.matrix.structure.attributes

Check warning on line 61 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L61

Added line #L61 was not covered by tests
& ATT_TRIANGLE_MASK == ATT_UPPER_TRIANGLE)

function Base.getindex(M::AASparseMatrix, i::Int, j::Int)
@assert all((1, 1) .<= (i,j) .<= size(M))
(startCol, endCol) = (M._colptr[j], M._colptr[j+1]-1) .+ 1
rowsInCol = @view M._rowval[startCol:endCol]
ind = searchsortedfirst(rowsInCol, i-1)
if ind <= length(rowsInCol) && rowsInCol[ind] == i-1
return M._nzval[startCol+ind-1]

Check warning on line 70 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L64-L70

Added lines #L64 - L70 were not covered by tests
end
return zero(eltype(M))

Check warning on line 72 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L72

Added line #L72 was not covered by tests
end

function Base.getindex(M::AASparseMatrix, i::Int)
@assert 1 <= i <= size(M)[1]*size(M)[2]
return M[(i-1) % size(M)[1] + 1, div(i-1, size(M)[1]) + 1]

Check warning on line 77 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L75-L77

Added lines #L75 - L77 were not covered by tests
end
# Creates a new structure, referring to the same data,
# but with the transpose flag (in attributes) flipped.
# TODO: untested
Base.transpose(M::AASparseMatrix) = AASparseMatrix(SparseGetTranspose(M.matrix),

Check warning on line 82 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L82

Added line #L82 was not covered by tests
M._colptr, M._rowval, M._nzval)

function Base.:(*)(A::AASparseMatrix{T}, x::StridedVecOrMat{T}) where T<:vTypes
@assert size(x)[1] == size(A)[2]
y = Array{T}(undef, size(A)[1], size(x)[2:end]...)
SparseMultiply(A.matrix, x, y)
return y

Check warning on line 89 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L85-L89

Added lines #L85 - L89 were not covered by tests
end

function Base.:(*)(alpha::T, A::AASparseMatrix{T},

Check warning on line 92 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L92

Added line #L92 was not covered by tests
x::StridedVecOrMat{T}) where T<:vTypes
@assert size(x)[1] == size(A)[2]
y = Array{T}(undef, size(A)[1], size(x)[2:end]...)
SparseMultiply(alpha, A.matrix, x, y)
return y

Check warning on line 97 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L94-L97

Added lines #L94 - L97 were not covered by tests
end

"""
Computes y += A*x in place. Note that this modifies its LAST argument.
"""
function muladd!(A::AASparseMatrix{T}, x::StridedVecOrMat{T},

Check warning on line 103 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L103

Added line #L103 was not covered by tests
y::StridedVecOrMat{T}) where T<:vTypes
@assert size(x) == size(y) && size(x)[1] == size(A)[2]
SparseMultiplyAdd(A.matrix, x, y)

Check warning on line 106 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L105-L106

Added lines #L105 - L106 were not covered by tests
end

"""
Computes y += alpha*A*x in place. Note that this modifies its LAST argument.
"""
function muladd!(alpha::T, A::AASparseMatrix{T},

Check warning on line 112 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L112

Added line #L112 was not covered by tests
x::StridedVecOrMat{T}, y::StridedVecOrMat{T}) where T<:vTypes
@assert size(x) == size(y) && size(x)[1] == size(A)[2]
SparseMultiplyAdd(alpha, A.matrix, x, y)

Check warning on line 115 in src/libSparse/AASparseMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/libSparse/AASparseMatrices.jl#L114-L115

Added lines #L114 - L115 were not covered by tests
end
Loading
Loading