fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
abstract type AbstractSparseArray{Tv,Ti,N} <: AbstractArray{Tv,N} end
|
||||
|
||||
const AbstractSparseVector{Tv,Ti} = AbstractSparseArray{Tv,Ti,1}
|
||||
const AbstractSparseMatrix{Tv,Ti} = AbstractSparseArray{Tv,Ti,2}
|
||||
|
||||
"""
|
||||
issparse(S)
|
||||
|
||||
Returns `true` if `S` is sparse, and `false` otherwise.
|
||||
"""
|
||||
issparse(A::AbstractArray) = false
|
||||
issparse(S::AbstractSparseArray) = true
|
||||
|
||||
issparse(S::Symmetric{<:Any,<:AbstractSparseMatrix}) = true
|
||||
issparse(S::Hermitian{<:Any,<:AbstractSparseMatrix}) = true
|
||||
issparse(S::LowerTriangular{<:Any,<:AbstractSparseMatrix}) = true
|
||||
issparse(S::LinAlg.UnitLowerTriangular{<:Any,<:AbstractSparseMatrix}) = true
|
||||
issparse(S::UpperTriangular{<:Any,<:AbstractSparseMatrix}) = true
|
||||
issparse(S::LinAlg.UnitUpperTriangular{<:Any,<:AbstractSparseMatrix}) = true
|
||||
|
||||
indtype(S::AbstractSparseArray{<:Any,Ti}) where {Ti} = Ti
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,79 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
## CHOLMOD
|
||||
const TRUE = Int32(1)
|
||||
const FALSE = Int32(0)
|
||||
|
||||
## itype defines the types of integer used:
|
||||
const INT = Int32(0) # all integer arrays are int
|
||||
const INTLONG = Int32(1) # most are int, some are SuiteSparse_long
|
||||
const LONG = Int32(2) # all integer arrays are SuiteSparse_long
|
||||
ityp(::Type{Int32}) = INT
|
||||
ityp(::Type{Int64}) = LONG
|
||||
|
||||
## dtype defines what the numerical type is (double or float):
|
||||
const DOUBLE = Int32(0) # all numerical values are double
|
||||
const SINGLE = Int32(1) # all numerical values are float
|
||||
dtyp(::Type{Float32}) = SINGLE
|
||||
dtyp(::Type{Float64}) = DOUBLE
|
||||
dtyp(::Type{Complex64}) = SINGLE
|
||||
dtyp(::Type{Complex128}) = DOUBLE
|
||||
|
||||
## xtype defines the kind of numerical values used:
|
||||
const PATTERN = Int32(0) # pattern only, no numerical values
|
||||
const REAL = Int32(1) # a real matrix
|
||||
const COMPLEX = Int32(2) # a complex matrix (ANSI C99 compatible)
|
||||
const ZOMPLEX = Int32(3) # a complex matrix (MATLAB compatible)
|
||||
xtyp(::Type{Float32}) = REAL
|
||||
xtyp(::Type{Float64}) = REAL
|
||||
xtyp(::Type{Complex64}) = COMPLEX
|
||||
xtyp(::Type{Complex128}) = COMPLEX
|
||||
|
||||
## Scaling modes, selected by the scale input parameter:
|
||||
const SCALAR = Int32(0) # A = s*A
|
||||
const ROW = Int32(1) # A = diag(s)*A
|
||||
const COL = Int32(2) # A = A*diag(s)
|
||||
const SYM = Int32(3) # A = diag(s)*A*diag(s)
|
||||
|
||||
## Types of systems to solve
|
||||
const CHOLMOD_A = Int32(0) # solve Ax=b
|
||||
const CHOLMOD_LDLt = Int32(1) # solve LDL'x=b
|
||||
const CHOLMOD_LD = Int32(2) # solve LDx=b
|
||||
const CHOLMOD_DLt = Int32(3) # solve DL'x=b
|
||||
const CHOLMOD_L = Int32(4) # solve Lx=b
|
||||
const CHOLMOD_Lt = Int32(5) # solve L'x=b
|
||||
const CHOLMOD_D = Int32(6) # solve Dx=b
|
||||
const CHOLMOD_P = Int32(7) # permute x=Px
|
||||
const CHOLMOD_Pt = Int32(8) # permute x=P'x
|
||||
|
||||
# Symmetry types
|
||||
const EMPTY =-1
|
||||
const MM_RECTANGULAR = 1
|
||||
const MM_UNSYMMETRIC = 2
|
||||
const MM_SYMMETRIC = 3
|
||||
const MM_HERMITIAN = 4
|
||||
const MM_SKEW_SYMMETRIC = 5
|
||||
const MM_SYMMETRIC_POSDIAG = 6
|
||||
const MM_HERMITIAN_POSDIAG = 7
|
||||
|
||||
# check the size of SuiteSparse_long
|
||||
if Int(ccall((:jl_cholmod_sizeof_long, :libsuitesparse_wrapper),Csize_t,())) == 4
|
||||
const SuiteSparse_long = Int32
|
||||
const IndexTypes = (:Int32,)
|
||||
const ITypes = Union{Int32}
|
||||
else
|
||||
const SuiteSparse_long = Int64
|
||||
const IndexTypes = (:Int32, :Int64)
|
||||
const ITypes = Union{Int32, Int64}
|
||||
end
|
||||
|
||||
const VTypes = Union{Complex128, Float64}
|
||||
const VRealTypes = Union{Float64}
|
||||
|
||||
mutable struct CHOLMODException <: Exception
|
||||
msg::AbstractString
|
||||
end
|
||||
|
||||
macro isok(A)
|
||||
:($(esc(A)) == TRUE || throw(CHOLMODException("")))
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,925 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
import Base.LinAlg: checksquare
|
||||
|
||||
## Functions to switch to 0-based indexing to call external sparse solvers
|
||||
|
||||
# Convert from 1-based to 0-based indices
|
||||
function decrement!(A::AbstractArray{T}) where T<:Integer
|
||||
for i in 1:length(A); A[i] -= oneunit(T) end
|
||||
A
|
||||
end
|
||||
decrement(A::AbstractArray{<:Integer}) = decrement!(copy(A))
|
||||
|
||||
# Convert from 0-based to 1-based indices
|
||||
function increment!(A::AbstractArray{T}) where T<:Integer
|
||||
for i in 1:length(A); A[i] += oneunit(T) end
|
||||
A
|
||||
end
|
||||
increment(A::AbstractArray{<:Integer}) = increment!(copy(A))
|
||||
|
||||
## sparse matrix multiplication
|
||||
|
||||
function (*)(A::SparseMatrixCSC{TvA,TiA}, B::SparseMatrixCSC{TvB,TiB}) where {TvA,TiA,TvB,TiB}
|
||||
(*)(sppromote(A, B)...)
|
||||
end
|
||||
for f in (:A_mul_Bt, :A_mul_Bc,
|
||||
:At_mul_B, :Ac_mul_B,
|
||||
:At_mul_Bt, :Ac_mul_Bc)
|
||||
@eval begin
|
||||
function ($f)(A::SparseMatrixCSC{TvA,TiA}, B::SparseMatrixCSC{TvB,TiB}) where {TvA,TiA,TvB,TiB}
|
||||
($f)(sppromote(A, B)...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sppromote(A::SparseMatrixCSC{TvA,TiA}, B::SparseMatrixCSC{TvB,TiB}) where {TvA,TiA,TvB,TiB}
|
||||
Tv = promote_type(TvA, TvB)
|
||||
Ti = promote_type(TiA, TiB)
|
||||
A = convert(SparseMatrixCSC{Tv,Ti}, A)
|
||||
B = convert(SparseMatrixCSC{Tv,Ti}, B)
|
||||
A, B
|
||||
end
|
||||
|
||||
# In matrix-vector multiplication, the correct orientation of the vector is assumed.
|
||||
|
||||
for (f, op, transp) in ((:A_mul_B, :identity, false),
|
||||
(:Ac_mul_B, :ctranspose, true),
|
||||
(:At_mul_B, :transpose, true))
|
||||
@eval begin
|
||||
function $(Symbol(f,:!))(α::Number, A::SparseMatrixCSC, B::StridedVecOrMat, β::Number, C::StridedVecOrMat)
|
||||
if $transp
|
||||
A.n == size(C, 1) || throw(DimensionMismatch())
|
||||
A.m == size(B, 1) || throw(DimensionMismatch())
|
||||
else
|
||||
A.n == size(B, 1) || throw(DimensionMismatch())
|
||||
A.m == size(C, 1) || throw(DimensionMismatch())
|
||||
end
|
||||
size(B, 2) == size(C, 2) || throw(DimensionMismatch())
|
||||
nzv = A.nzval
|
||||
rv = A.rowval
|
||||
if β != 1
|
||||
β != 0 ? scale!(C, β) : fill!(C, zero(eltype(C)))
|
||||
end
|
||||
for k = 1:size(C, 2)
|
||||
for col = 1:A.n
|
||||
if $transp
|
||||
tmp = zero(eltype(C))
|
||||
@inbounds for j = A.colptr[col]:(A.colptr[col + 1] - 1)
|
||||
tmp += $(op)(nzv[j])*B[rv[j],k]
|
||||
end
|
||||
C[col,k] += α*tmp
|
||||
else
|
||||
αxj = α*B[col,k]
|
||||
@inbounds for j = A.colptr[col]:(A.colptr[col + 1] - 1)
|
||||
C[rv[j], k] += nzv[j]*αxj
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
C
|
||||
end
|
||||
|
||||
function $(f)(A::SparseMatrixCSC{TA,S}, x::StridedVector{Tx}) where {TA,S,Tx}
|
||||
T = promote_type(TA, Tx)
|
||||
$(Symbol(f,:!))(one(T), A, x, zero(T), similar(x, T, A.n))
|
||||
end
|
||||
function $(f)(A::SparseMatrixCSC{TA,S}, B::StridedMatrix{Tx}) where {TA,S,Tx}
|
||||
T = promote_type(TA, Tx)
|
||||
$(Symbol(f,:!))(one(T), A, B, zero(T), similar(B, T, (A.n, size(B, 2))))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# For compatibility with dense multiplication API. Should be deleted when dense multiplication
|
||||
# API is updated to follow BLAS API.
|
||||
A_mul_B!(C::StridedVecOrMat, A::SparseMatrixCSC, B::StridedVecOrMat) = A_mul_B!(one(eltype(B)), A, B, zero(eltype(C)), C)
|
||||
Ac_mul_B!(C::StridedVecOrMat, A::SparseMatrixCSC, B::StridedVecOrMat) = Ac_mul_B!(one(eltype(B)), A, B, zero(eltype(C)), C)
|
||||
At_mul_B!(C::StridedVecOrMat, A::SparseMatrixCSC, B::StridedVecOrMat) = At_mul_B!(one(eltype(B)), A, B, zero(eltype(C)), C)
|
||||
|
||||
|
||||
function (*)(X::StridedMatrix{TX}, A::SparseMatrixCSC{TvA,TiA}) where {TX,TvA,TiA}
|
||||
mX, nX = size(X)
|
||||
nX == A.m || throw(DimensionMismatch())
|
||||
Y = zeros(promote_type(TX,TvA), mX, A.n)
|
||||
rowval = A.rowval
|
||||
nzval = A.nzval
|
||||
@inbounds for multivec_row=1:mX, col = 1:A.n, k=A.colptr[col]:(A.colptr[col+1]-1)
|
||||
Y[multivec_row, col] += X[multivec_row, rowval[k]] * nzval[k]
|
||||
end
|
||||
Y
|
||||
end
|
||||
|
||||
function (*)(D::Diagonal, A::SparseMatrixCSC)
|
||||
T = Base.promote_op(*, eltype(D), eltype(A))
|
||||
scale!(LinAlg.copy_oftype(A, T), D.diag, A)
|
||||
end
|
||||
function (*)(A::SparseMatrixCSC, D::Diagonal)
|
||||
T = Base.promote_op(*, eltype(D), eltype(A))
|
||||
scale!(LinAlg.copy_oftype(A, T), A, D.diag)
|
||||
end
|
||||
|
||||
# Sparse matrix multiplication as described in [Gustavson, 1978]:
|
||||
# http://dl.acm.org/citation.cfm?id=355796
|
||||
|
||||
(*)(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = spmatmul(A,B)
|
||||
for (f, opA, opB) in ((:A_mul_Bt, :identity, :transpose),
|
||||
(:A_mul_Bc, :identity, :ctranspose),
|
||||
(:At_mul_B, :transpose, :identity),
|
||||
(:Ac_mul_B, :ctranspose, :identity),
|
||||
(:At_mul_Bt, :transpose, :transpose),
|
||||
(:Ac_mul_Bc, :ctranspose, :ctranspose))
|
||||
@eval begin
|
||||
function ($f)(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti}
|
||||
spmatmul(($opA)(A), ($opB)(B))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function spmatmul(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti};
|
||||
sortindices::Symbol = :sortcols) where {Tv,Ti}
|
||||
mA, nA = size(A)
|
||||
mB, nB = size(B)
|
||||
nA==mB || throw(DimensionMismatch())
|
||||
|
||||
colptrA = A.colptr; rowvalA = A.rowval; nzvalA = A.nzval
|
||||
colptrB = B.colptr; rowvalB = B.rowval; nzvalB = B.nzval
|
||||
# TODO: Need better estimation of result space
|
||||
nnzC = min(mA*nB, length(nzvalA) + length(nzvalB))
|
||||
colptrC = Vector{Ti}(nB+1)
|
||||
rowvalC = Vector{Ti}(nnzC)
|
||||
nzvalC = Vector{Tv}(nnzC)
|
||||
|
||||
@inbounds begin
|
||||
ip = 1
|
||||
xb = zeros(Ti, mA)
|
||||
x = zeros(Tv, mA)
|
||||
for i in 1:nB
|
||||
if ip + mA - 1 > nnzC
|
||||
resize!(rowvalC, nnzC + max(nnzC,mA))
|
||||
resize!(nzvalC, nnzC + max(nnzC,mA))
|
||||
nnzC = length(nzvalC)
|
||||
end
|
||||
colptrC[i] = ip
|
||||
for jp in colptrB[i]:(colptrB[i+1] - 1)
|
||||
nzB = nzvalB[jp]
|
||||
j = rowvalB[jp]
|
||||
for kp in colptrA[j]:(colptrA[j+1] - 1)
|
||||
nzC = nzvalA[kp] * nzB
|
||||
k = rowvalA[kp]
|
||||
if xb[k] != i
|
||||
rowvalC[ip] = k
|
||||
ip += 1
|
||||
xb[k] = i
|
||||
x[k] = nzC
|
||||
else
|
||||
x[k] += nzC
|
||||
end
|
||||
end
|
||||
end
|
||||
for vp in colptrC[i]:(ip - 1)
|
||||
nzvalC[vp] = x[rowvalC[vp]]
|
||||
end
|
||||
end
|
||||
colptrC[nB+1] = ip
|
||||
end
|
||||
|
||||
deleteat!(rowvalC, colptrC[end]:length(rowvalC))
|
||||
deleteat!(nzvalC, colptrC[end]:length(nzvalC))
|
||||
|
||||
# The Gustavson algorithm does not guarantee the product to have sorted row indices.
|
||||
Cunsorted = SparseMatrixCSC(mA, nB, colptrC, rowvalC, nzvalC)
|
||||
C = SparseArrays.sortSparseMatrixCSC!(Cunsorted, sortindices=sortindices)
|
||||
return C
|
||||
end
|
||||
|
||||
## solvers
|
||||
function fwdTriSolve!(A::SparseMatrixCSC, B::AbstractVecOrMat)
|
||||
# forward substitution for CSC matrices
|
||||
nrowB, ncolB = size(B, 1), size(B, 2)
|
||||
ncol = LinAlg.checksquare(A)
|
||||
if nrowB != ncol
|
||||
throw(DimensionMismatch("A is $(ncol) columns and B has $(nrowB) rows"))
|
||||
end
|
||||
|
||||
aa = A.nzval
|
||||
ja = A.rowval
|
||||
ia = A.colptr
|
||||
|
||||
joff = 0
|
||||
for k = 1:ncolB
|
||||
for j = 1:nrowB
|
||||
i1 = ia[j]
|
||||
i2 = ia[j + 1] - 1
|
||||
|
||||
# loop through the structural zeros
|
||||
ii = i1
|
||||
jai = ja[ii]
|
||||
while ii <= i2 && jai < j
|
||||
ii += 1
|
||||
jai = ja[ii]
|
||||
end
|
||||
|
||||
# check for zero pivot and divide with pivot
|
||||
if jai == j
|
||||
bj = B[joff + jai]/aa[ii]
|
||||
B[joff + jai] = bj
|
||||
ii += 1
|
||||
else
|
||||
throw(LinAlg.SingularException(j))
|
||||
end
|
||||
|
||||
# update remaining part
|
||||
for i = ii:i2
|
||||
B[joff + ja[i]] -= bj*aa[i]
|
||||
end
|
||||
end
|
||||
joff += nrowB
|
||||
end
|
||||
B
|
||||
end
|
||||
|
||||
function bwdTriSolve!(A::SparseMatrixCSC, B::AbstractVecOrMat)
|
||||
# backward substitution for CSC matrices
|
||||
nrowB, ncolB = size(B, 1), size(B, 2)
|
||||
ncol = LinAlg.checksquare(A)
|
||||
if nrowB != ncol
|
||||
throw(DimensionMismatch("A is $(ncol) columns and B has $(nrowB) rows"))
|
||||
end
|
||||
|
||||
aa = A.nzval
|
||||
ja = A.rowval
|
||||
ia = A.colptr
|
||||
|
||||
joff = 0
|
||||
for k = 1:ncolB
|
||||
for j = nrowB:-1:1
|
||||
i1 = ia[j]
|
||||
i2 = ia[j + 1] - 1
|
||||
|
||||
# loop through the structural zeros
|
||||
ii = i2
|
||||
jai = ja[ii]
|
||||
while ii >= i1 && jai > j
|
||||
ii -= 1
|
||||
jai = ja[ii]
|
||||
end
|
||||
|
||||
# check for zero pivot and divide with pivot
|
||||
if jai == j
|
||||
bj = B[joff + jai]/aa[ii]
|
||||
B[joff + jai] = bj
|
||||
ii -= 1
|
||||
else
|
||||
throw(LinAlg.SingularException(j))
|
||||
end
|
||||
|
||||
# update remaining part
|
||||
for i = ii:-1:i1
|
||||
B[joff + ja[i]] -= bj*aa[i]
|
||||
end
|
||||
end
|
||||
joff += nrowB
|
||||
end
|
||||
B
|
||||
end
|
||||
|
||||
A_ldiv_B!(L::LowerTriangular{T,<:SparseMatrixCSC{T}}, B::StridedVecOrMat) where {T} = fwdTriSolve!(L.data, B)
|
||||
A_ldiv_B!(U::UpperTriangular{T,<:SparseMatrixCSC{T}}, B::StridedVecOrMat) where {T} = bwdTriSolve!(U.data, B)
|
||||
|
||||
(\)(L::LowerTriangular{T,<:SparseMatrixCSC{T}}, B::SparseMatrixCSC) where {T} = A_ldiv_B!(L, Array(B))
|
||||
(\)(U::UpperTriangular{T,<:SparseMatrixCSC{T}}, B::SparseMatrixCSC) where {T} = A_ldiv_B!(U, Array(B))
|
||||
|
||||
## triu, tril
|
||||
|
||||
function triu(S::SparseMatrixCSC{Tv,Ti}, k::Integer=0) where {Tv,Ti}
|
||||
m,n = size(S)
|
||||
if (k > 0 && k > n) || (k < 0 && -k > m)
|
||||
throw(BoundsError())
|
||||
end
|
||||
colptr = Vector{Ti}(n+1)
|
||||
nnz = 0
|
||||
for col = 1 : min(max(k+1,1), n+1)
|
||||
colptr[col] = 1
|
||||
end
|
||||
for col = max(k+1,1) : n
|
||||
for c1 = S.colptr[col] : S.colptr[col+1]-1
|
||||
S.rowval[c1] > col - k && break
|
||||
nnz += 1
|
||||
end
|
||||
colptr[col+1] = nnz+1
|
||||
end
|
||||
rowval = Vector{Ti}(nnz)
|
||||
nzval = Vector{Tv}(nnz)
|
||||
A = SparseMatrixCSC(m, n, colptr, rowval, nzval)
|
||||
for col = max(k+1,1) : n
|
||||
c1 = S.colptr[col]
|
||||
for c2 = A.colptr[col] : A.colptr[col+1]-1
|
||||
A.rowval[c2] = S.rowval[c1]
|
||||
A.nzval[c2] = S.nzval[c1]
|
||||
c1 += 1
|
||||
end
|
||||
end
|
||||
A
|
||||
end
|
||||
|
||||
function tril(S::SparseMatrixCSC{Tv,Ti}, k::Integer=0) where {Tv,Ti}
|
||||
m,n = size(S)
|
||||
if (k > 0 && k > n) || (k < 0 && -k > m)
|
||||
throw(BoundsError())
|
||||
end
|
||||
colptr = Vector{Ti}(n+1)
|
||||
nnz = 0
|
||||
colptr[1] = 1
|
||||
for col = 1 : min(n, m+k)
|
||||
l1 = S.colptr[col+1]-1
|
||||
for c1 = 0 : (l1 - S.colptr[col])
|
||||
S.rowval[l1 - c1] < col - k && break
|
||||
nnz += 1
|
||||
end
|
||||
colptr[col+1] = nnz+1
|
||||
end
|
||||
for col = max(min(n, m+k)+2,1) : n+1
|
||||
colptr[col] = nnz+1
|
||||
end
|
||||
rowval = Vector{Ti}(nnz)
|
||||
nzval = Vector{Tv}(nnz)
|
||||
A = SparseMatrixCSC(m, n, colptr, rowval, nzval)
|
||||
for col = 1 : min(n, m+k)
|
||||
c1 = S.colptr[col+1]-1
|
||||
l2 = A.colptr[col+1]-1
|
||||
for c2 = 0 : l2 - A.colptr[col]
|
||||
A.rowval[l2 - c2] = S.rowval[c1]
|
||||
A.nzval[l2 - c2] = S.nzval[c1]
|
||||
c1 -= 1
|
||||
end
|
||||
end
|
||||
A
|
||||
end
|
||||
|
||||
## diff
|
||||
|
||||
function sparse_diff1(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti}
|
||||
m,n = size(S)
|
||||
m > 1 || return SparseMatrixCSC(0, n, ones(Ti,n+1), Ti[], Tv[])
|
||||
colptr = Vector{Ti}(n+1)
|
||||
numnz = 2 * nnz(S) # upper bound; will shrink later
|
||||
rowval = Vector{Ti}(numnz)
|
||||
nzval = Vector{Tv}(numnz)
|
||||
numnz = 0
|
||||
colptr[1] = 1
|
||||
for col = 1 : n
|
||||
last_row = 0
|
||||
last_val = 0
|
||||
for k = S.colptr[col] : S.colptr[col+1]-1
|
||||
row = S.rowval[k]
|
||||
val = S.nzval[k]
|
||||
if row > 1
|
||||
if row == last_row + 1
|
||||
nzval[numnz] += val
|
||||
nzval[numnz]==zero(Tv) && (numnz -= 1)
|
||||
else
|
||||
numnz += 1
|
||||
rowval[numnz] = row - 1
|
||||
nzval[numnz] = val
|
||||
end
|
||||
end
|
||||
if row < m
|
||||
numnz += 1
|
||||
rowval[numnz] = row
|
||||
nzval[numnz] = -val
|
||||
end
|
||||
last_row = row
|
||||
last_val = val
|
||||
end
|
||||
colptr[col+1] = numnz+1
|
||||
end
|
||||
deleteat!(rowval, numnz+1:length(rowval))
|
||||
deleteat!(nzval, numnz+1:length(nzval))
|
||||
return SparseMatrixCSC(m-1, n, colptr, rowval, nzval)
|
||||
end
|
||||
|
||||
function sparse_diff2(a::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti}
|
||||
m,n = size(a)
|
||||
colptr = Vector{Ti}(max(n,1))
|
||||
numnz = 2 * nnz(a) # upper bound; will shrink later
|
||||
rowval = Vector{Ti}(numnz)
|
||||
nzval = Vector{Tv}(numnz)
|
||||
|
||||
z = zero(Tv)
|
||||
|
||||
colptr_a = a.colptr
|
||||
rowval_a = a.rowval
|
||||
nzval_a = a.nzval
|
||||
|
||||
ptrS = 1
|
||||
colptr[1] = 1
|
||||
|
||||
n == 0 && return SparseMatrixCSC(m, n, colptr, rowval, nzval)
|
||||
|
||||
startA = colptr_a[1]
|
||||
stopA = colptr_a[2]
|
||||
|
||||
rA = startA : stopA - 1
|
||||
rowvalA = rowval_a[rA]
|
||||
nzvalA = nzval_a[rA]
|
||||
lA = stopA - startA
|
||||
|
||||
for col = 1:n-1
|
||||
startB, stopB = startA, stopA
|
||||
startA = colptr_a[col+1]
|
||||
stopA = colptr_a[col+2]
|
||||
|
||||
rowvalB = rowvalA
|
||||
nzvalB = nzvalA
|
||||
lB = lA
|
||||
|
||||
rA = startA : stopA - 1
|
||||
rowvalA = rowval_a[rA]
|
||||
nzvalA = nzval_a[rA]
|
||||
lA = stopA - startA
|
||||
|
||||
ptrB = 1
|
||||
ptrA = 1
|
||||
|
||||
while ptrA <= lA && ptrB <= lB
|
||||
rowA = rowvalA[ptrA]
|
||||
rowB = rowvalB[ptrB]
|
||||
if rowA < rowB
|
||||
rowval[ptrS] = rowA
|
||||
nzval[ptrS] = nzvalA[ptrA]
|
||||
ptrS += 1
|
||||
ptrA += 1
|
||||
elseif rowB < rowA
|
||||
rowval[ptrS] = rowB
|
||||
nzval[ptrS] = -nzvalB[ptrB]
|
||||
ptrS += 1
|
||||
ptrB += 1
|
||||
else
|
||||
res = nzvalA[ptrA] - nzvalB[ptrB]
|
||||
if res != z
|
||||
rowval[ptrS] = rowA
|
||||
nzval[ptrS] = res
|
||||
ptrS += 1
|
||||
end
|
||||
ptrA += 1
|
||||
ptrB += 1
|
||||
end
|
||||
end
|
||||
|
||||
while ptrA <= lA
|
||||
rowval[ptrS] = rowvalA[ptrA]
|
||||
nzval[ptrS] = nzvalA[ptrA]
|
||||
ptrS += 1
|
||||
ptrA += 1
|
||||
end
|
||||
|
||||
while ptrB <= lB
|
||||
rowval[ptrS] = rowvalB[ptrB]
|
||||
nzval[ptrS] = -nzvalB[ptrB]
|
||||
ptrS += 1
|
||||
ptrB += 1
|
||||
end
|
||||
|
||||
colptr[col+1] = ptrS
|
||||
end
|
||||
deleteat!(rowval, ptrS:length(rowval))
|
||||
deleteat!(nzval, ptrS:length(nzval))
|
||||
return SparseMatrixCSC(m, n-1, colptr, rowval, nzval)
|
||||
end
|
||||
|
||||
diff(a::SparseMatrixCSC, dim::Integer)= dim==1 ? sparse_diff1(a) : sparse_diff2(a)
|
||||
|
||||
## norm and rank
|
||||
vecnorm(A::SparseMatrixCSC, p::Real=2) = vecnorm(A.nzval, p)
|
||||
|
||||
function norm(A::SparseMatrixCSC,p::Real=2)
|
||||
m, n = size(A)
|
||||
if m == 0 || n == 0 || isempty(A)
|
||||
return float(real(zero(eltype(A))))
|
||||
elseif m == 1 || n == 1
|
||||
# TODO: compute more efficiently using A.nzval directly
|
||||
return norm(Array(A), p)
|
||||
else
|
||||
Tnorm = typeof(float(real(zero(eltype(A)))))
|
||||
Tsum = promote_type(Float64,Tnorm)
|
||||
if p==1
|
||||
nA::Tsum = 0
|
||||
for j=1:n
|
||||
colSum::Tsum = 0
|
||||
for i = A.colptr[j]:A.colptr[j+1]-1
|
||||
colSum += abs(A.nzval[i])
|
||||
end
|
||||
nA = max(nA, colSum)
|
||||
end
|
||||
return convert(Tnorm, nA)
|
||||
elseif p==2
|
||||
throw(ArgumentError("2-norm not yet implemented for sparse matrices. Try norm(Array(A)) or norm(A, p) where p=1 or Inf."))
|
||||
elseif p==Inf
|
||||
rowSum = zeros(Tsum,m)
|
||||
for i=1:length(A.nzval)
|
||||
rowSum[A.rowval[i]] += abs(A.nzval[i])
|
||||
end
|
||||
return convert(Tnorm, maximum(rowSum))
|
||||
end
|
||||
end
|
||||
throw(ArgumentError("invalid p-norm p=$p. Valid: 1, Inf"))
|
||||
end
|
||||
|
||||
# TODO rank
|
||||
|
||||
# cond
|
||||
function cond(A::SparseMatrixCSC, p::Real=2)
|
||||
if p == 1
|
||||
normAinv = normestinv(A)
|
||||
normA = norm(A, 1)
|
||||
return normA * normAinv
|
||||
elseif p == Inf
|
||||
normAinv = normestinv(A')
|
||||
normA = norm(A, Inf)
|
||||
return normA * normAinv
|
||||
elseif p == 2
|
||||
throw(ArgumentError("2-norm condition number is not implemented for sparse matrices, try cond(Array(A), 2) instead"))
|
||||
else
|
||||
throw(ArgumentError("second argument must be either 1 or Inf, got $p"))
|
||||
end
|
||||
end
|
||||
|
||||
function normestinv(A::SparseMatrixCSC{T}, t::Integer = min(2,maximum(size(A)))) where T
|
||||
maxiter = 5
|
||||
# Check the input
|
||||
n = checksquare(A)
|
||||
F = factorize(A)
|
||||
if t <= 0
|
||||
throw(ArgumentError("number of blocks must be a positive integer"))
|
||||
end
|
||||
if t > n
|
||||
throw(ArgumentError("number of blocks must not be greater than $n"))
|
||||
end
|
||||
ind = Vector{Int64}(n)
|
||||
ind_hist = Vector{Int64}(maxiter * t)
|
||||
|
||||
Ti = typeof(float(zero(T)))
|
||||
|
||||
S = zeros(T <: Real ? Int : Ti, n, t)
|
||||
|
||||
function _rand_pm1!(v)
|
||||
for i in eachindex(v)
|
||||
v[i] = rand()<0.5?1:-1
|
||||
end
|
||||
end
|
||||
|
||||
function _any_abs_eq(v,n::Int)
|
||||
for vv in v
|
||||
if abs(vv)==n
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
# Generate the block matrix
|
||||
X = Matrix{Ti}(n, t)
|
||||
X[1:n,1] = 1
|
||||
for j = 2:t
|
||||
while true
|
||||
_rand_pm1!(view(X,1:n,j))
|
||||
yaux = X[1:n,j]' * X[1:n,1:j-1]
|
||||
if !_any_abs_eq(yaux,n)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
scale!(X, 1./n)
|
||||
|
||||
iter = 0
|
||||
local est
|
||||
local est_old
|
||||
est_ind = 0
|
||||
while iter < maxiter
|
||||
iter += 1
|
||||
Y = F \ X
|
||||
est = zero(real(eltype(Y)))
|
||||
est_ind = 0
|
||||
for i = 1:t
|
||||
y = norm(Y[1:n,i], 1)
|
||||
if y > est
|
||||
est = y
|
||||
est_ind = i
|
||||
end
|
||||
end
|
||||
if iter == 1
|
||||
est_old = est
|
||||
end
|
||||
if est > est_old || iter == 2
|
||||
ind_best = est_ind
|
||||
end
|
||||
if iter >= 2 && est <= est_old
|
||||
est = est_old
|
||||
break
|
||||
end
|
||||
est_old = est
|
||||
S_old = copy(S)
|
||||
for j = 1:t
|
||||
for i = 1:n
|
||||
S[i,j] = Y[i,j]==0?one(Y[i,j]):sign(Y[i,j])
|
||||
end
|
||||
end
|
||||
|
||||
if T <: Real
|
||||
# Check whether cols of S are parallel to cols of S or S_old
|
||||
for j = 1:t
|
||||
while true
|
||||
repeated = false
|
||||
if j > 1
|
||||
saux = S[1:n,j]' * S[1:n,1:j-1]
|
||||
if _any_abs_eq(saux,n)
|
||||
repeated = true
|
||||
end
|
||||
end
|
||||
if !repeated
|
||||
saux2 = S[1:n,j]' * S_old[1:n,1:t]
|
||||
if _any_abs_eq(saux2,n)
|
||||
repeated = true
|
||||
end
|
||||
end
|
||||
if repeated
|
||||
_rand_pm1!(view(S,1:n,j))
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Use the conjugate transpose
|
||||
Z = F' \ S
|
||||
h_max = zero(real(eltype(Z)))
|
||||
h = zeros(real(eltype(Z)), n)
|
||||
h_ind = 0
|
||||
for i = 1:n
|
||||
h[i] = norm(Z[i,1:t], Inf)
|
||||
if h[i] > h_max
|
||||
h_max = h[i]
|
||||
h_ind = i
|
||||
end
|
||||
ind[i] = i
|
||||
end
|
||||
if iter >=2 && ind_best == h_ind
|
||||
break
|
||||
end
|
||||
p = sortperm(h, rev=true)
|
||||
h = h[p]
|
||||
permute!(ind, p)
|
||||
if t > 1
|
||||
addcounter = t
|
||||
elemcounter = 0
|
||||
while addcounter > 0 && elemcounter < n
|
||||
elemcounter = elemcounter + 1
|
||||
current_element = ind[elemcounter]
|
||||
found = false
|
||||
for i = 1:t * (iter - 1)
|
||||
if current_element == ind_hist[i]
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if !found
|
||||
addcounter = addcounter - 1
|
||||
for i = 1:current_element - 1
|
||||
X[i,t-addcounter] = 0
|
||||
end
|
||||
X[current_element,t-addcounter] = 1
|
||||
for i = current_element + 1:n
|
||||
X[i,t-addcounter] = 0
|
||||
end
|
||||
ind_hist[iter * t - addcounter] = current_element
|
||||
else
|
||||
if elemcounter == t && addcounter == t
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
ind_hist[1:t] = ind[1:t]
|
||||
for j = 1:t
|
||||
for i = 1:ind[j] - 1
|
||||
X[i,j] = 0
|
||||
end
|
||||
X[ind[j],j] = 1
|
||||
for i = ind[j] + 1:n
|
||||
X[i,j] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return est
|
||||
end
|
||||
|
||||
# kron
|
||||
|
||||
function kron(a::SparseMatrixCSC{Tv,Ti}, b::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti}
|
||||
numnzA = nnz(a)
|
||||
numnzB = nnz(b)
|
||||
|
||||
numnz = numnzA * numnzB
|
||||
|
||||
mA,nA = size(a)
|
||||
mB,nB = size(b)
|
||||
|
||||
m,n = mA*mB, nA*nB
|
||||
|
||||
colptr = Vector{Ti}(n+1)
|
||||
rowval = Vector{Ti}(numnz)
|
||||
nzval = Vector{Tv}(numnz)
|
||||
|
||||
colptr[1] = 1
|
||||
|
||||
colptrA = a.colptr
|
||||
colptrB = b.colptr
|
||||
rowvalA = a.rowval
|
||||
rowvalB = b.rowval
|
||||
nzvalA = a.nzval
|
||||
nzvalB = b.nzval
|
||||
|
||||
col = 1
|
||||
|
||||
@inbounds for j = 1:nA
|
||||
startA = colptrA[j]
|
||||
stopA = colptrA[j+1]-1
|
||||
lA = stopA - startA + 1
|
||||
|
||||
for i = 1:nB
|
||||
startB = colptrB[i]
|
||||
stopB = colptrB[i+1]-1
|
||||
lB = stopB - startB + 1
|
||||
|
||||
ptr_range = (1:lB) + (colptr[col]-1)
|
||||
|
||||
colptr[col+1] = colptr[col] + lA * lB
|
||||
col += 1
|
||||
|
||||
for ptrA = startA : stopA
|
||||
ptrB = startB
|
||||
for ptr = ptr_range
|
||||
rowval[ptr] = (rowvalA[ptrA]-1)*mB + rowvalB[ptrB]
|
||||
nzval[ptr] = nzvalA[ptrA] * nzvalB[ptrB]
|
||||
ptrB += 1
|
||||
end
|
||||
ptr_range += lB
|
||||
end
|
||||
end
|
||||
end
|
||||
SparseMatrixCSC(m, n, colptr, rowval, nzval)
|
||||
end
|
||||
|
||||
function kron(A::SparseMatrixCSC{Tv1,Ti1}, B::SparseMatrixCSC{Tv2,Ti2}) where {Tv1,Ti1,Tv2,Ti2}
|
||||
Tv_res = promote_type(Tv1, Tv2)
|
||||
Ti_res = promote_type(Ti1, Ti2)
|
||||
A = convert(SparseMatrixCSC{Tv_res,Ti_res}, A)
|
||||
B = convert(SparseMatrixCSC{Tv_res,Ti_res}, B)
|
||||
return kron(A,B)
|
||||
end
|
||||
|
||||
kron(A::SparseMatrixCSC, B::VecOrMat) = kron(A, sparse(B))
|
||||
kron(A::VecOrMat, B::SparseMatrixCSC) = kron(sparse(A), B)
|
||||
|
||||
## det, inv, cond
|
||||
|
||||
inv(A::SparseMatrixCSC) = error("The inverse of a sparse matrix can often be dense and can cause the computer to run out of memory. If you are sure you have enough memory, please convert your matrix to a dense matrix.")
|
||||
|
||||
# TODO
|
||||
|
||||
## scale methods
|
||||
|
||||
# Copy colptr and rowval from one sparse matrix to another
|
||||
function copyinds!(C::SparseMatrixCSC, A::SparseMatrixCSC)
|
||||
if C.colptr !== A.colptr
|
||||
resize!(C.colptr, length(A.colptr))
|
||||
copy!(C.colptr, A.colptr)
|
||||
end
|
||||
if C.rowval !== A.rowval
|
||||
resize!(C.rowval, length(A.rowval))
|
||||
copy!(C.rowval, A.rowval)
|
||||
end
|
||||
end
|
||||
|
||||
# multiply by diagonal matrix as vector
|
||||
function scale!(C::SparseMatrixCSC, A::SparseMatrixCSC, b::Vector)
|
||||
m, n = size(A)
|
||||
(n==length(b) && size(A)==size(C)) || throw(DimensionMismatch())
|
||||
copyinds!(C, A)
|
||||
Cnzval = C.nzval
|
||||
Anzval = A.nzval
|
||||
resize!(Cnzval, length(Anzval))
|
||||
for col = 1:n, p = A.colptr[col]:(A.colptr[col+1]-1)
|
||||
@inbounds Cnzval[p] = Anzval[p] * b[col]
|
||||
end
|
||||
C
|
||||
end
|
||||
|
||||
function scale!(C::SparseMatrixCSC, b::Vector, A::SparseMatrixCSC)
|
||||
m, n = size(A)
|
||||
(m==length(b) && size(A)==size(C)) || throw(DimensionMismatch())
|
||||
copyinds!(C, A)
|
||||
Cnzval = C.nzval
|
||||
Anzval = A.nzval
|
||||
Arowval = A.rowval
|
||||
resize!(Cnzval, length(Anzval))
|
||||
for col = 1:n, p = A.colptr[col]:(A.colptr[col+1]-1)
|
||||
@inbounds Cnzval[p] = Anzval[p] * b[Arowval[p]]
|
||||
end
|
||||
C
|
||||
end
|
||||
|
||||
function scale!(C::SparseMatrixCSC, A::SparseMatrixCSC, b::Number)
|
||||
size(A)==size(C) || throw(DimensionMismatch())
|
||||
copyinds!(C, A)
|
||||
resize!(C.nzval, length(A.nzval))
|
||||
scale!(C.nzval, A.nzval, b)
|
||||
C
|
||||
end
|
||||
|
||||
function scale!(C::SparseMatrixCSC, b::Number, A::SparseMatrixCSC)
|
||||
size(A)==size(C) || throw(DimensionMismatch())
|
||||
copyinds!(C, A)
|
||||
resize!(C.nzval, length(A.nzval))
|
||||
scale!(C.nzval, b, A.nzval)
|
||||
C
|
||||
end
|
||||
|
||||
scale!(A::SparseMatrixCSC, b::Number) = (scale!(A.nzval, b); A)
|
||||
scale!(b::Number, A::SparseMatrixCSC) = (scale!(b, A.nzval); A)
|
||||
|
||||
for f in (:\, :Ac_ldiv_B, :At_ldiv_B)
|
||||
@eval begin
|
||||
function ($f)(A::SparseMatrixCSC, B::AbstractVecOrMat)
|
||||
m, n = size(A)
|
||||
if m == n
|
||||
if istril(A)
|
||||
if istriu(A)
|
||||
return ($f)(Diagonal(A), B)
|
||||
else
|
||||
return ($f)(LowerTriangular(A), B)
|
||||
end
|
||||
elseif istriu(A)
|
||||
return ($f)(UpperTriangular(A), B)
|
||||
end
|
||||
if ishermitian(A)
|
||||
return ($f)(Hermitian(A), B)
|
||||
end
|
||||
return ($f)(lufact(A), B)
|
||||
else
|
||||
return ($f)(qrfact(A), B)
|
||||
end
|
||||
end
|
||||
($f)(::SparseMatrixCSC, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
|
||||
end
|
||||
end
|
||||
|
||||
function factorize(A::SparseMatrixCSC)
|
||||
m, n = size(A)
|
||||
if m == n
|
||||
if istril(A)
|
||||
if istriu(A)
|
||||
return Diagonal(A)
|
||||
else
|
||||
return LowerTriangular(A)
|
||||
end
|
||||
elseif istriu(A)
|
||||
return UpperTriangular(A)
|
||||
end
|
||||
if ishermitian(A)
|
||||
try
|
||||
return cholfact(Hermitian(A))
|
||||
catch e
|
||||
isa(e, PosDefException) || rethrow(e)
|
||||
return ldltfact(Hermitian(A))
|
||||
end
|
||||
end
|
||||
return lufact(A)
|
||||
else
|
||||
return qrfact(A)
|
||||
end
|
||||
end
|
||||
|
||||
function factorize{Ti}(A::Symmetric{Float64,SparseMatrixCSC{Float64,Ti}})
|
||||
try
|
||||
return cholfact(A)
|
||||
catch e
|
||||
isa(e, PosDefException) || rethrow(e)
|
||||
return ldltfact(A)
|
||||
end
|
||||
end
|
||||
function factorize{Ti}(A::Hermitian{Complex{Float64}, SparseMatrixCSC{Complex{Float64},Ti}})
|
||||
try
|
||||
return cholfact(A)
|
||||
catch e
|
||||
isa(e, PosDefException) || rethrow(e)
|
||||
return ldltfact(A)
|
||||
end
|
||||
end
|
||||
|
||||
chol(A::SparseMatrixCSC) = error("Use cholfact() instead of chol() for sparse matrices.")
|
||||
lu(A::SparseMatrixCSC) = error("Use lufact() instead of lu() for sparse matrices.")
|
||||
eig(A::SparseMatrixCSC) = error("Use eigs() instead of eig() for sparse matrices.")
|
||||
@@ -0,0 +1,46 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module SparseArrays
|
||||
|
||||
using Base: ReshapedArray, promote_op, setindex_shape_check, to_shape, tail
|
||||
using Base.Sort: Forward
|
||||
using Base.LinAlg: AbstractTriangular, PosDefException
|
||||
|
||||
import Base: +, -, *, \, /, &, |, xor, ==
|
||||
import Base: A_mul_B!, Ac_mul_B, Ac_mul_B!, At_mul_B, At_mul_B!
|
||||
import Base: A_mul_Bc, A_mul_Bt, Ac_mul_Bc, At_mul_Bt
|
||||
import Base: At_ldiv_B, Ac_ldiv_B, A_ldiv_B!
|
||||
import Base.LinAlg: At_ldiv_B!, Ac_ldiv_B!
|
||||
|
||||
import Base: @get!, acos, acosd, acot, acotd, acsch, asech, asin, asind, asinh,
|
||||
atan, atand, atanh, broadcast!, chol, conj!, cos, cosc, cosd, cosh, cospi, cot,
|
||||
cotd, coth, countnz, csc, cscd, csch, ctranspose!, diag, diff, done, dot, eig,
|
||||
exp10, exp2, eye, findn, floor, hash, indmin, inv, issymmetric, istril, istriu,
|
||||
log10, log2, lu, next, sec, secd, sech, show, sin,
|
||||
sinc, sind, sinh, sinpi, squeeze, start, sum, summary, tan,
|
||||
tand, tanh, trace, transpose!, tril!, triu!, trunc, vecnorm, abs, abs2,
|
||||
broadcast, ceil, complex, cond, conj, convert, copy, copy!, ctranspose, diagm,
|
||||
exp, expm1, factorize, find, findmax, findmin, findnz, float, full, getindex,
|
||||
vcat, hcat, hvcat, cat, imag, indmax, ishermitian, kron, length, log, log1p, max, min,
|
||||
maximum, minimum, norm, one, promote_eltype, real, reinterpret, reshape, rot180,
|
||||
rotl90, rotr90, round, scale!, setindex!, similar, size, transpose, tril,
|
||||
triu, vec, permute!, map, map!
|
||||
|
||||
export AbstractSparseArray, AbstractSparseMatrix, AbstractSparseVector,
|
||||
SparseMatrixCSC, SparseVector, blkdiag, droptol!, dropzeros!, dropzeros,
|
||||
issparse, nonzeros, nzrange, rowvals, sparse, sparsevec, spdiagm, speye, spones,
|
||||
sprand, sprandn, spzeros, nnz, permute
|
||||
|
||||
include("abstractsparse.jl")
|
||||
include("sparsematrix.jl")
|
||||
include("sparsevector.jl")
|
||||
include("higherorderfns.jl")
|
||||
|
||||
include("linalg.jl")
|
||||
if Base.USE_GPL_LIBS
|
||||
include("umfpack.jl")
|
||||
include("cholmod.jl")
|
||||
include("spqr.jl")
|
||||
end
|
||||
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,184 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module SPQR
|
||||
|
||||
import Base: \
|
||||
|
||||
# ordering options */
|
||||
const ORDERING_FIXED = Int32(0)
|
||||
const ORDERING_NATURAL = Int32(1)
|
||||
const ORDERING_COLAMD = Int32(2)
|
||||
const ORDERING_GIVEN = Int32(3) # only used for C/C++ interface
|
||||
const ORDERING_CHOLMOD = Int32(4) # CHOLMOD best-effort (COLAMD, METIS,...)
|
||||
const ORDERING_AMD = Int32(5) # AMD(A'*A)
|
||||
const ORDERING_METIS = Int32(6) # metis(A'*A)
|
||||
const ORDERING_DEFAULT = Int32(7) # SuiteSparseQR default ordering
|
||||
const ORDERING_BEST = Int32(8) # try COLAMD, AMD, and METIS; pick best
|
||||
const ORDERING_BESTAMD = Int32(9) # try COLAMD and AMD; pick best#
|
||||
|
||||
# Let [m n] = size of the matrix after pruning singletons. The default
|
||||
# ordering strategy is to use COLAMD if m <= 2*n. Otherwise, AMD(A'A) is
|
||||
# tried. If there is a high fill-in with AMD then try METIS(A'A) and take
|
||||
# the best of AMD and METIS. METIS is not tried if it isn't installed.
|
||||
|
||||
# tol options
|
||||
const DEFAULT_TOL = Int32(-2) # if tol <= -2, the default tol is used
|
||||
const NO_TOL = Int32(-1) # if -2 < tol < 0, then no tol is used
|
||||
|
||||
# for qmult, method can be 0,1,2,3:
|
||||
const QTX = Int32(0)
|
||||
const QX = Int32(1)
|
||||
const XQT = Int32(2)
|
||||
const XQ = Int32(3)
|
||||
|
||||
# system can be 0,1,2,3: Given Q*R=A*E from SuiteSparseQR_factorize:
|
||||
const RX_EQUALS_B = Int32(0) # solve R*X=B or X = R\B
|
||||
const RETX_EQUALS_B = Int32(1) # solve R*E'*X=B or X = E*(R\B)
|
||||
const RTX_EQUALS_B = Int32(2) # solve R'*X=B or X = R'\B
|
||||
const RTX_EQUALS_ETB = Int32(3) # solve R'*X=E'*B or X = R'\(E'*B)
|
||||
|
||||
|
||||
using ..SparseArrays: SparseMatrixCSC
|
||||
using ..SparseArrays.CHOLMOD: C_Dense, C_Sparse, Dense, ITypes, Sparse, SuiteSparseStruct, VTypes, common
|
||||
|
||||
import Base: size
|
||||
import Base.LinAlg: qrfact
|
||||
import ..SparseArrays.CHOLMOD: convert, free!
|
||||
|
||||
struct C_Factorization{Tv<:VTypes} <: SuiteSparseStruct
|
||||
xtype::Cint
|
||||
factors::Ptr{Tv}
|
||||
end
|
||||
|
||||
mutable struct Factorization{Tv<:VTypes} <: Base.LinAlg.Factorization{Tv}
|
||||
m::Int
|
||||
n::Int
|
||||
p::Ptr{C_Factorization{Tv}}
|
||||
function Factorization{Tv}(m::Integer, n::Integer, p::Ptr{C_Factorization{Tv}}) where Tv<:VTypes
|
||||
if p == C_NULL
|
||||
throw(ArgumentError("factorization failed for unknown reasons. Please submit a bug report."))
|
||||
end
|
||||
new(m, n, p)
|
||||
end
|
||||
end
|
||||
Factorization(m::Integer, n::Integer, p::Ptr{C_Factorization{Tv}}) where Tv<:VTypes = Factorization{Tv}(m, n, p)
|
||||
|
||||
size(F::Factorization) = (F.m, F.n)
|
||||
function size(F::Factorization, i::Integer)
|
||||
if i < 1
|
||||
throw(ArgumentError("dimension must be positive"))
|
||||
end
|
||||
if i == 1
|
||||
return F.m
|
||||
elseif i == 2
|
||||
return F.n
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
function free!(F::Factorization{Tv}) where Tv<:VTypes
|
||||
ccall((:SuiteSparseQR_C_free, :libspqr), Cint,
|
||||
(Ptr{Ptr{C_Factorization{Tv}}}, Ptr{Void}),
|
||||
&F.p, common()) == 1
|
||||
end
|
||||
|
||||
function backslash(ordering::Integer, tol::Real, A::Sparse{Tv}, B::Dense{Tv}) where Tv<:VTypes
|
||||
m, n = size(A)
|
||||
if m != size(B, 1)
|
||||
throw(DimensionMismatch("left hand side and right hand side must have same number of rows"))
|
||||
end
|
||||
d = Dense(ccall((:SuiteSparseQR_C_backslash, :libspqr), Ptr{C_Dense{Tv}},
|
||||
(Cint, Cdouble, Ptr{C_Sparse{Tv}}, Ptr{C_Dense{Tv}}, Ptr{Void}),
|
||||
ordering, tol, get(A.p), get(B.p), common()))
|
||||
finalizer(d, free!)
|
||||
d
|
||||
end
|
||||
|
||||
function factorize(ordering::Integer, tol::Real, A::Sparse{Tv}) where Tv<:VTypes
|
||||
s = unsafe_load(A.p)
|
||||
if s.stype != 0
|
||||
throw(ArgumentError("stype must be zero"))
|
||||
end
|
||||
f = Factorization(size(A)..., ccall((:SuiteSparseQR_C_factorize, :libspqr), Ptr{C_Factorization{Tv}},
|
||||
(Cint, Cdouble, Ptr{Sparse{Tv}}, Ptr{Void}),
|
||||
ordering, tol, get(A.p), common()))
|
||||
finalizer(f, free!)
|
||||
f
|
||||
end
|
||||
|
||||
function solve(system::Integer, QR::Factorization{Tv}, B::Dense{Tv}) where Tv<:VTypes
|
||||
m, n = size(QR)
|
||||
mB = size(B, 1)
|
||||
if (system == RX_EQUALS_B || system == RETX_EQUALS_B) && m != mB
|
||||
throw(DimensionMismatch("number of rows in factorized matrix must equal number of rows in right hand side"))
|
||||
elseif (system == RTX_EQUALS_ETB || system == RTX_EQUALS_B) && n != mB
|
||||
throw(DimensionMismatch("number of columns in factorized matrix must equal number of rows in right hand side"))
|
||||
end
|
||||
d = Dense(ccall((:SuiteSparseQR_C_solve, :libspqr), Ptr{C_Dense{Tv}},
|
||||
(Cint, Ptr{C_Factorization{Tv}}, Ptr{C_Dense{Tv}}, Ptr{Void}),
|
||||
system, get(QR.p), get(B.p), common()))
|
||||
finalizer(d, free!)
|
||||
d
|
||||
end
|
||||
|
||||
function qmult(method::Integer, QR::Factorization{Tv}, X::Dense{Tv}) where Tv<:VTypes
|
||||
mQR, nQR = size(QR)
|
||||
mX, nX = size(X)
|
||||
if (method == QTX || method == QX) && mQR != mX
|
||||
throw(DimensionMismatch("Q matrix size $mQR and dense matrix has $mX rows"))
|
||||
elseif (method == XQT || method == XQ) && mQR != nX
|
||||
throw(DimensionMismatch("Q matrix size $mQR and dense matrix has $nX columns"))
|
||||
end
|
||||
d = Dense(ccall((:SuiteSparseQR_C_qmult, :libspqr), Ptr{C_Dense{Tv}},
|
||||
(Cint, Ptr{C_Factorization{Tv}}, Ptr{C_Dense{Tv}}, Ptr{Void}),
|
||||
method, get(QR.p), get(X.p), common()))
|
||||
finalizer(d, free!)
|
||||
d
|
||||
end
|
||||
|
||||
|
||||
qrfact(A::SparseMatrixCSC, ::Type{Val{true}}) = factorize(ORDERING_DEFAULT, DEFAULT_TOL, Sparse(A, 0))
|
||||
|
||||
"""
|
||||
qrfact(A) -> SPQR.Factorization
|
||||
|
||||
Compute the `QR` factorization of a sparse matrix `A`. A fill-reducing permutation is used.
|
||||
The main application of this type is to solve least squares problems with [`\\`](@ref). The function
|
||||
calls the C library SPQR and a few additional functions from the library are wrapped but not
|
||||
exported.
|
||||
"""
|
||||
qrfact(A::SparseMatrixCSC) = qrfact(A, Val{true})
|
||||
|
||||
# With a real lhs and complex rhs with the same precision, we can reinterpret
|
||||
# the complex rhs as a real rhs with twice the number of columns
|
||||
#
|
||||
# This definition is similar to the definition in factorization.jl except that
|
||||
# here we have to use \ instead of A_ldiv_B! because of limitations in SPQR
|
||||
|
||||
## Two helper methods
|
||||
_ret_size(F::Factorization, b::AbstractVector) = (size(F, 2),)
|
||||
_ret_size(F::Factorization, B::AbstractMatrix) = (size(F, 2), size(B, 2))
|
||||
|
||||
function (\)(F::Factorization{Float64}, B::VecOrMat{Complex{Float64}})
|
||||
# |z1|z3| reinterpret |x1|x2|x3|x4| transpose |x1|y1| reshape |x1|y1|x3|y3|
|
||||
# |z2|z4| -> |y1|y2|y3|y4| -> |x2|y2| -> |x2|y2|x4|y4|
|
||||
# |x3|y3|
|
||||
# |x4|y4|
|
||||
c2r = reshape(transpose(reinterpret(Float64, B, (2, length(B)))), size(B, 1), 2*size(B, 2))
|
||||
x = F\c2r
|
||||
|
||||
# |z1|z3| reinterpret |x1|x2|x3|x4| transpose |x1|y1| reshape |x1|y1|x3|y3|
|
||||
# |z2|z4| <- |y1|y2|y3|y4| <- |x2|y2| <- |x2|y2|x4|y4|
|
||||
# |x3|y3|
|
||||
# |x4|y4|
|
||||
return reinterpret(Complex{Float64}, transpose(reshape(x, (length(x) >> 1), 2)), _ret_size(F, B))
|
||||
end
|
||||
|
||||
function (\)(F::Factorization{T}, B::StridedVecOrMat{T}) where T<:VTypes
|
||||
QtB = qmult(QTX, F, Dense(B))
|
||||
convert(typeof(B), solve(RETX_EQUALS_B, F, QtB))
|
||||
end
|
||||
|
||||
(\)(F::Factorization, B::StridedVecOrMat) = F\convert(AbstractArray{eltype(F)}, B)
|
||||
|
||||
end # module
|
||||
@@ -0,0 +1,522 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module UMFPACK
|
||||
|
||||
export UmfpackLU
|
||||
|
||||
import Base: (\), Ac_ldiv_B, At_ldiv_B, findnz, getindex, show, size
|
||||
import Base.LinAlg: A_ldiv_B!, Ac_ldiv_B!, At_ldiv_B!, Factorization, det, lufact
|
||||
|
||||
importall ..SparseArrays
|
||||
import ..SparseArrays: increment, increment!, decrement, decrement!, nnz
|
||||
|
||||
include("umfpack_h.jl")
|
||||
mutable struct MatrixIllConditionedException <: Exception
|
||||
message::AbstractString
|
||||
end
|
||||
|
||||
function umferror(status::Integer)
|
||||
if status==UMFPACK_OK
|
||||
return
|
||||
elseif status==UMFPACK_WARNING_singular_matrix
|
||||
throw(LinAlg.SingularException(0))
|
||||
elseif status==UMFPACK_WARNING_determinant_underflow
|
||||
throw(MatrixIllConditionedException("the determinant is nonzero but underflowed"))
|
||||
elseif status==UMFPACK_WARNING_determinant_overflow
|
||||
throw(MatrixIllConditionedException("the determinant overflowed"))
|
||||
elseif status==UMFPACK_ERROR_out_of_memory
|
||||
throw(OutOfMemoryError())
|
||||
elseif status==UMFPACK_ERROR_invalid_Numeric_object
|
||||
throw(ArgumentError("invalid UMFPack numeric object"))
|
||||
elseif status==UMFPACK_ERROR_invalid_Symbolic_object
|
||||
throw(ArgumentError("invalid UMFPack symbolic object"))
|
||||
elseif status==UMFPACK_ERROR_argument_missing
|
||||
throw(ArgumentError("a required argument to UMFPack is missing"))
|
||||
elseif status==UMFPACK_ERROR_n_nonpositive
|
||||
throw(ArgumentError("the number of rows or columns of the matrix must be greater than zero"))
|
||||
elseif status==UMFPACK_ERROR_invalid_matrix
|
||||
throw(ArgumentError("invalid matrix"))
|
||||
elseif status==UMFPACK_ERROR_different_pattern
|
||||
throw(ArgumentError("pattern of the matrix changed"))
|
||||
elseif status==UMFPACK_ERROR_invalid_system
|
||||
throw(ArgumentError("invalid sys argument provided to UMFPack solver"))
|
||||
elseif status==UMFPACK_ERROR_invalid_permutation
|
||||
throw(ArgumentError("invalid permutation"))
|
||||
elseif status==UMFPACK_ERROR_file_IO
|
||||
throw(ErrorException("error saving / loading UMFPack decomposition"))
|
||||
elseif status==UMFPACK_ERROR_ordering_failed
|
||||
throw(ErrorException("the ordering method failed"))
|
||||
elseif status==UMFPACK_ERROR_internal_error
|
||||
throw(ErrorException("an internal error has occurred, of unknown cause"))
|
||||
else
|
||||
throw(ErrorException("unknown UMFPack error code: $status"))
|
||||
end
|
||||
end
|
||||
|
||||
macro isok(A)
|
||||
:(umferror($(esc(A))))
|
||||
end
|
||||
|
||||
# check the size of SuiteSparse_long
|
||||
if Int(ccall((:jl_cholmod_sizeof_long,:libsuitesparse_wrapper),Csize_t,())) == 4
|
||||
const UmfpackIndexTypes = (:Int32,)
|
||||
const UMFITypes = Int32
|
||||
else
|
||||
const UmfpackIndexTypes = (:Int32, :Int64)
|
||||
const UMFITypes = Union{Int32, Int64}
|
||||
end
|
||||
|
||||
const UMFVTypes = Union{Float64,Complex128}
|
||||
|
||||
## UMFPACK
|
||||
|
||||
# the control and info arrays
|
||||
const umf_ctrl = Vector{Float64}(UMFPACK_CONTROL)
|
||||
ccall((:umfpack_dl_defaults,:libumfpack), Void, (Ptr{Float64},), umf_ctrl)
|
||||
const umf_info = Vector{Float64}(UMFPACK_INFO)
|
||||
|
||||
function show_umf_ctrl(level::Real = 2.0)
|
||||
old_prt::Float64 = umf_ctrl[1]
|
||||
umf_ctrl[1] = Float64(level)
|
||||
ccall((:umfpack_dl_report_control, :libumfpack), Void, (Ptr{Float64},), umf_ctrl)
|
||||
umf_ctrl[1] = old_prt
|
||||
end
|
||||
|
||||
function show_umf_info(level::Real = 2.0)
|
||||
old_prt::Float64 = umf_ctrl[1]
|
||||
umf_ctrl[1] = Float64(level)
|
||||
ccall((:umfpack_dl_report_info, :libumfpack), Void,
|
||||
(Ptr{Float64}, Ptr{Float64}), umf_ctrl, umf_info)
|
||||
umf_ctrl[1] = old_prt
|
||||
end
|
||||
|
||||
## Should this type be immutable?
|
||||
mutable struct UmfpackLU{Tv<:UMFVTypes,Ti<:UMFITypes} <: Factorization{Tv}
|
||||
symbolic::Ptr{Void}
|
||||
numeric::Ptr{Void}
|
||||
m::Int
|
||||
n::Int
|
||||
colptr::Vector{Ti} # 0-based column pointers
|
||||
rowval::Vector{Ti} # 0-based row indices
|
||||
nzval::Vector{Tv}
|
||||
end
|
||||
|
||||
"""
|
||||
lufact(A::SparseMatrixCSC) -> F::UmfpackLU
|
||||
|
||||
Compute the LU factorization of a sparse matrix `A`.
|
||||
|
||||
For sparse `A` with real or complex element type, the return type of `F` is
|
||||
`UmfpackLU{Tv, Ti}`, with `Tv` = [`Float64`](@ref) or `Complex128` respectively and
|
||||
`Ti` is an integer type ([`Int32`](@ref) or [`Int64`](@ref)).
|
||||
|
||||
The individual components of the factorization `F` can be accessed by indexing:
|
||||
|
||||
| Component | Description |
|
||||
|:----------|:------------------------------------|
|
||||
| `F[:L]` | `L` (lower triangular) part of `LU` |
|
||||
| `F[:U]` | `U` (upper triangular) part of `LU` |
|
||||
| `F[:p]` | right permutation `Vector` |
|
||||
| `F[:q]` | left permutation `Vector` |
|
||||
| `F[:Rs]` | `Vector` of scaling factors |
|
||||
| `F[:(:)]` | `(L,U,p,q,Rs)` components |
|
||||
|
||||
The relation between `F` and `A` is
|
||||
|
||||
`F[:L]*F[:U] == (F[:Rs] .* A)[F[:p], F[:q]]`
|
||||
|
||||
`F` further supports the following functions:
|
||||
|
||||
- [`\\`](@ref)
|
||||
- [`cond`](@ref)
|
||||
- [`det`](@ref)
|
||||
|
||||
!!! note
|
||||
`lufact(A::SparseMatrixCSC)` uses the UMFPACK library that is part of
|
||||
SuiteSparse. As this library only supports sparse matrices with [`Float64`](@ref) or
|
||||
`Complex128` elements, `lufact` converts `A` into a copy that is of type
|
||||
`SparseMatrixCSC{Float64}` or `SparseMatrixCSC{Complex128}` as appropriate.
|
||||
"""
|
||||
function lufact(S::SparseMatrixCSC{<:UMFVTypes,<:UMFITypes})
|
||||
zerobased = S.colptr[1] == 0
|
||||
res = UmfpackLU(C_NULL, C_NULL, S.m, S.n,
|
||||
zerobased ? copy(S.colptr) : decrement(S.colptr),
|
||||
zerobased ? copy(S.rowval) : decrement(S.rowval),
|
||||
copy(S.nzval))
|
||||
finalizer(res, umfpack_free_symbolic)
|
||||
umfpack_numeric!(res)
|
||||
end
|
||||
lufact{Ti<:UMFITypes}(A::SparseMatrixCSC{<:Union{Float16,Float32},Ti}) =
|
||||
lufact(convert(SparseMatrixCSC{Float64,Ti}, A))
|
||||
lufact{Ti<:UMFITypes}(A::SparseMatrixCSC{<:Union{Complex32,Complex64},Ti}) =
|
||||
lufact(convert(SparseMatrixCSC{Complex128,Ti}, A))
|
||||
lufact{T<:AbstractFloat}(A::Union{SparseMatrixCSC{T},SparseMatrixCSC{Complex{T}}}) =
|
||||
throw(ArgumentError(string("matrix type ", typeof(A), "not supported. ",
|
||||
"Try lufact(convert(SparseMatrixCSC{Float64/Complex128,Int}, A)) for ",
|
||||
"sparse floating point LU using UMFPACK or lufact(Array(A)) for generic ",
|
||||
"dense LU.")))
|
||||
lufact(A::SparseMatrixCSC) = lufact(float(A))
|
||||
|
||||
|
||||
size(F::UmfpackLU) = (F.m, F.n)
|
||||
function size(F::UmfpackLU, dim::Integer)
|
||||
if dim < 1
|
||||
throw(ArgumentError("size: dimension $dim out of range"))
|
||||
elseif dim == 1
|
||||
return Int(F.m)
|
||||
elseif dim == 2
|
||||
return Int(F.n)
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
function show(io::IO, F::UmfpackLU)
|
||||
println(io, "UMFPACK LU Factorization of a $(size(F)) sparse matrix")
|
||||
F.numeric != C_NULL && println(io, F.numeric)
|
||||
end
|
||||
|
||||
## Wrappers for UMFPACK functions
|
||||
|
||||
# generate the name of the C function according to the value and integer types
|
||||
umf_nm(nm,Tv,Ti) = "umfpack_" * (Tv == :Float64 ? "d" : "z") * (Ti == :Int64 ? "l_" : "i_") * nm
|
||||
|
||||
for itype in UmfpackIndexTypes
|
||||
sym_r = umf_nm("symbolic", :Float64, itype)
|
||||
sym_c = umf_nm("symbolic", :Complex128, itype)
|
||||
num_r = umf_nm("numeric", :Float64, itype)
|
||||
num_c = umf_nm("numeric", :Complex128, itype)
|
||||
sol_r = umf_nm("solve", :Float64, itype)
|
||||
sol_c = umf_nm("solve", :Complex128, itype)
|
||||
det_r = umf_nm("get_determinant", :Float64, itype)
|
||||
det_z = umf_nm("get_determinant", :Complex128, itype)
|
||||
lunz_r = umf_nm("get_lunz", :Float64, itype)
|
||||
lunz_z = umf_nm("get_lunz", :Complex128, itype)
|
||||
get_num_r = umf_nm("get_numeric", :Float64, itype)
|
||||
get_num_z = umf_nm("get_numeric", :Complex128, itype)
|
||||
@eval begin
|
||||
function umfpack_symbolic!(U::UmfpackLU{Float64,$itype})
|
||||
if U.symbolic != C_NULL return U end
|
||||
tmp = Vector{Ptr{Void}}(1)
|
||||
@isok ccall(($sym_r, :libumfpack), $itype,
|
||||
($itype, $itype, Ptr{$itype}, Ptr{$itype}, Ptr{Float64}, Ptr{Void},
|
||||
Ptr{Float64}, Ptr{Float64}),
|
||||
U.m, U.n, U.colptr, U.rowval, U.nzval, tmp,
|
||||
umf_ctrl, umf_info)
|
||||
U.symbolic = tmp[1]
|
||||
return U
|
||||
end
|
||||
function umfpack_symbolic!(U::UmfpackLU{Complex128,$itype})
|
||||
if U.symbolic != C_NULL return U end
|
||||
tmp = Vector{Ptr{Void}}(1)
|
||||
@isok ccall(($sym_c, :libumfpack), $itype,
|
||||
($itype, $itype, Ptr{$itype}, Ptr{$itype}, Ptr{Float64}, Ptr{Float64}, Ptr{Void},
|
||||
Ptr{Float64}, Ptr{Float64}),
|
||||
U.m, U.n, U.colptr, U.rowval, real(U.nzval), imag(U.nzval), tmp,
|
||||
umf_ctrl, umf_info)
|
||||
U.symbolic = tmp[1]
|
||||
return U
|
||||
end
|
||||
function umfpack_numeric!(U::UmfpackLU{Float64,$itype})
|
||||
if U.numeric != C_NULL return U end
|
||||
if U.symbolic == C_NULL umfpack_symbolic!(U) end
|
||||
tmp = Vector{Ptr{Void}}(1)
|
||||
status = ccall(($num_r, :libumfpack), $itype,
|
||||
(Ptr{$itype}, Ptr{$itype}, Ptr{Float64}, Ptr{Void}, Ptr{Void},
|
||||
Ptr{Float64}, Ptr{Float64}),
|
||||
U.colptr, U.rowval, U.nzval, U.symbolic, tmp,
|
||||
umf_ctrl, umf_info)
|
||||
if status != UMFPACK_WARNING_singular_matrix
|
||||
umferror(status)
|
||||
end
|
||||
U.numeric = tmp[1]
|
||||
return U
|
||||
end
|
||||
function umfpack_numeric!(U::UmfpackLU{Complex128,$itype})
|
||||
if U.numeric != C_NULL return U end
|
||||
if U.symbolic == C_NULL umfpack_symbolic!(U) end
|
||||
tmp = Vector{Ptr{Void}}(1)
|
||||
status = ccall(($num_c, :libumfpack), $itype,
|
||||
(Ptr{$itype}, Ptr{$itype}, Ptr{Float64}, Ptr{Float64}, Ptr{Void}, Ptr{Void},
|
||||
Ptr{Float64}, Ptr{Float64}),
|
||||
U.colptr, U.rowval, real(U.nzval), imag(U.nzval), U.symbolic, tmp,
|
||||
umf_ctrl, umf_info)
|
||||
if status != UMFPACK_WARNING_singular_matrix
|
||||
umferror(status)
|
||||
end
|
||||
U.numeric = tmp[1]
|
||||
return U
|
||||
end
|
||||
function solve!(x::StridedVector{Float64}, lu::UmfpackLU{Float64,$itype}, b::StridedVector{Float64}, typ::Integer)
|
||||
if x === b
|
||||
throw(ArgumentError("output array must not be aliased with input array"))
|
||||
end
|
||||
if stride(x, 1) != 1 || stride(b, 1) != 1
|
||||
throw(ArgumentError("in and output vectors must have unit strides"))
|
||||
end
|
||||
umfpack_numeric!(lu)
|
||||
(size(b,1) == lu.m) && (size(b) == size(x)) || throw(DimensionMismatch())
|
||||
@isok ccall(($sol_r, :libumfpack), $itype,
|
||||
($itype, Ptr{$itype}, Ptr{$itype}, Ptr{Float64},
|
||||
Ptr{Float64}, Ptr{Float64}, Ptr{Void}, Ptr{Float64},
|
||||
Ptr{Float64}),
|
||||
typ, lu.colptr, lu.rowval, lu.nzval,
|
||||
x, b, lu.numeric, umf_ctrl,
|
||||
umf_info)
|
||||
return x
|
||||
end
|
||||
function solve!(x::StridedVector{Complex128}, lu::UmfpackLU{Complex128,$itype}, b::StridedVector{Complex128}, typ::Integer)
|
||||
if x === b
|
||||
throw(ArgumentError("output array must not be aliased with input array"))
|
||||
end
|
||||
if stride(x, 1) != 1 || stride(b, 1) != 1
|
||||
throw(ArgumentError("in and output vectors must have unit strides"))
|
||||
end
|
||||
umfpack_numeric!(lu)
|
||||
(size(b, 1) == lu.m) && (size(b) == size(x)) || throw(DimensionMismatch())
|
||||
n = size(b, 1)
|
||||
@isok ccall(($sol_c, :libumfpack), $itype,
|
||||
($itype, Ptr{$itype}, Ptr{$itype}, Ptr{Float64},
|
||||
Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, Ptr{Float64},
|
||||
Ptr{Float64}, Ptr{Void}, Ptr{Float64}, Ptr{Float64}),
|
||||
typ, lu.colptr, lu.rowval, lu.nzval,
|
||||
C_NULL, x, C_NULL, b,
|
||||
C_NULL, lu.numeric, umf_ctrl, umf_info)
|
||||
return x
|
||||
end
|
||||
function det(lu::UmfpackLU{Float64,$itype})
|
||||
mx = Ref{Float64}()
|
||||
@isok ccall(($det_r,:libumfpack), $itype,
|
||||
(Ptr{Float64},Ptr{Float64},Ptr{Void},Ptr{Float64}),
|
||||
mx, C_NULL, lu.numeric, umf_info)
|
||||
mx[]
|
||||
end
|
||||
function det(lu::UmfpackLU{Complex128,$itype})
|
||||
mx = Ref{Float64}()
|
||||
mz = Ref{Float64}()
|
||||
@isok ccall(($det_z,:libumfpack), $itype,
|
||||
(Ptr{Float64},Ptr{Float64},Ptr{Float64},Ptr{Void},Ptr{Float64}),
|
||||
mx, mz, C_NULL, lu.numeric, umf_info)
|
||||
complex(mx[], mz[])
|
||||
end
|
||||
function umf_lunz(lu::UmfpackLU{Float64,$itype})
|
||||
lnz = Ref{$itype}()
|
||||
unz = Ref{$itype}()
|
||||
n_row = Ref{$itype}()
|
||||
n_col = Ref{$itype}()
|
||||
nz_diag = Ref{$itype}()
|
||||
@isok ccall(($lunz_r,:libumfpack), $itype,
|
||||
(Ptr{$itype},Ptr{$itype},Ptr{$itype},Ptr{$itype},Ptr{$itype},Ptr{Void}),
|
||||
lnz, unz, n_row, n_col, nz_diag, lu.numeric)
|
||||
(lnz[], unz[], n_row[], n_col[], nz_diag[])
|
||||
end
|
||||
function umf_lunz(lu::UmfpackLU{Complex128,$itype})
|
||||
lnz = Ref{$itype}()
|
||||
unz = Ref{$itype}()
|
||||
n_row = Ref{$itype}()
|
||||
n_col = Ref{$itype}()
|
||||
nz_diag = Ref{$itype}()
|
||||
@isok ccall(($lunz_z,:libumfpack), $itype,
|
||||
(Ptr{$itype},Ptr{$itype},Ptr{$itype},Ptr{$itype},Ptr{$itype},Ptr{Void}),
|
||||
lnz, unz, n_row, n_col, nz_diag, lu.numeric)
|
||||
(lnz[], unz[], n_row[], n_col[], nz_diag[])
|
||||
end
|
||||
function umf_extract(lu::UmfpackLU{Float64,$itype})
|
||||
umfpack_numeric!(lu) # ensure the numeric decomposition exists
|
||||
(lnz, unz, n_row, n_col, nz_diag) = umf_lunz(lu)
|
||||
Lp = Vector{$itype}(n_row + 1)
|
||||
Lj = Vector{$itype}(lnz) # L is returned in CSR (compressed sparse row) format
|
||||
Lx = Vector{Float64}(lnz)
|
||||
Up = Vector{$itype}(n_col + 1)
|
||||
Ui = Vector{$itype}(unz)
|
||||
Ux = Vector{Float64}(unz)
|
||||
P = Vector{$itype}(n_row)
|
||||
Q = Vector{$itype}(n_col)
|
||||
Rs = Vector{Float64}(n_row)
|
||||
@isok ccall(($get_num_r,:libumfpack), $itype,
|
||||
(Ptr{$itype},Ptr{$itype},Ptr{Float64},
|
||||
Ptr{$itype},Ptr{$itype},Ptr{Float64},
|
||||
Ptr{$itype},Ptr{$itype},Ptr{Void},
|
||||
Ptr{$itype},Ptr{Float64},Ptr{Void}),
|
||||
Lp,Lj,Lx,
|
||||
Up,Ui,Ux,
|
||||
P, Q, C_NULL,
|
||||
&0, Rs, lu.numeric)
|
||||
(transpose(SparseMatrixCSC(min(n_row, n_col), n_row, increment!(Lp), increment!(Lj), Lx)),
|
||||
SparseMatrixCSC(min(n_row, n_col), n_col, increment!(Up), increment!(Ui), Ux),
|
||||
increment!(P), increment!(Q), Rs)
|
||||
end
|
||||
function umf_extract(lu::UmfpackLU{Complex128,$itype})
|
||||
umfpack_numeric!(lu) # ensure the numeric decomposition exists
|
||||
(lnz, unz, n_row, n_col, nz_diag) = umf_lunz(lu)
|
||||
Lp = Vector{$itype}(n_row + 1)
|
||||
Lj = Vector{$itype}(lnz) # L is returned in CSR (compressed sparse row) format
|
||||
Lx = Vector{Float64}(lnz)
|
||||
Lz = Vector{Float64}(lnz)
|
||||
Up = Vector{$itype}(n_col + 1)
|
||||
Ui = Vector{$itype}(unz)
|
||||
Ux = Vector{Float64}(unz)
|
||||
Uz = Vector{Float64}(unz)
|
||||
P = Vector{$itype}(n_row)
|
||||
Q = Vector{$itype}(n_col)
|
||||
Rs = Vector{Float64}(n_row)
|
||||
@isok ccall(($get_num_z,:libumfpack), $itype,
|
||||
(Ptr{$itype},Ptr{$itype},Ptr{Float64},Ptr{Float64},
|
||||
Ptr{$itype},Ptr{$itype},Ptr{Float64},Ptr{Float64},
|
||||
Ptr{$itype},Ptr{$itype},Ptr{Void}, Ptr{Void},
|
||||
Ptr{$itype},Ptr{Float64},Ptr{Void}),
|
||||
Lp,Lj,Lx,Lz,
|
||||
Up,Ui,Ux,Uz,
|
||||
P, Q, C_NULL, C_NULL,
|
||||
&0, Rs, lu.numeric)
|
||||
(transpose(SparseMatrixCSC(min(n_row, n_col), n_row, increment!(Lp), increment!(Lj), complex.(Lx, Lz))),
|
||||
SparseMatrixCSC(min(n_row, n_col), n_col, increment!(Up), increment!(Ui), complex.(Ux, Uz)),
|
||||
increment!(P), increment!(Q), Rs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function nnz(lu::UmfpackLU)
|
||||
lnz, unz, = umf_lunz(lu)
|
||||
return Int(lnz + unz)
|
||||
end
|
||||
|
||||
### Solve with Factorization
|
||||
A_ldiv_B!{T<:UMFVTypes}(lu::UmfpackLU{T}, B::StridedVecOrMat{T}) = A_ldiv_B!(B, lu, copy(B))
|
||||
At_ldiv_B!{T<:UMFVTypes}(lu::UmfpackLU{T}, B::StridedVecOrMat{T}) = At_ldiv_B!(B, lu, copy(B))
|
||||
Ac_ldiv_B!{T<:UMFVTypes}(lu::UmfpackLU{T}, B::StridedVecOrMat{T}) = Ac_ldiv_B!(B, lu, copy(B))
|
||||
A_ldiv_B!(lu::UmfpackLU{Float64}, B::StridedVecOrMat{<:Complex}) = A_ldiv_B!(B, lu, copy(B))
|
||||
At_ldiv_B!(lu::UmfpackLU{Float64}, B::StridedVecOrMat{<:Complex}) = At_ldiv_B!(B, lu, copy(B))
|
||||
Ac_ldiv_B!(lu::UmfpackLU{Float64}, B::StridedVecOrMat{<:Complex}) = Ac_ldiv_B!(B, lu, copy(B))
|
||||
|
||||
A_ldiv_B!{T<:UMFVTypes}(X::StridedVecOrMat{T}, lu::UmfpackLU{T}, B::StridedVecOrMat{T}) =
|
||||
_Aq_ldiv_B!(X, lu, B, UMFPACK_A)
|
||||
At_ldiv_B!{T<:UMFVTypes}(X::StridedVecOrMat{T}, lu::UmfpackLU{T}, B::StridedVecOrMat{T}) =
|
||||
_Aq_ldiv_B!(X, lu, B, UMFPACK_Aat)
|
||||
Ac_ldiv_B!{T<:UMFVTypes}(X::StridedVecOrMat{T}, lu::UmfpackLU{T}, B::StridedVecOrMat{T}) =
|
||||
_Aq_ldiv_B!(X, lu, B, UMFPACK_At)
|
||||
A_ldiv_B!{Tb<:Complex}(X::StridedVecOrMat{Tb}, lu::UmfpackLU{Float64}, B::StridedVecOrMat{Tb}) =
|
||||
_Aq_ldiv_B!(X, lu, B, UMFPACK_A)
|
||||
At_ldiv_B!{Tb<:Complex}(X::StridedVecOrMat{Tb}, lu::UmfpackLU{Float64}, B::StridedVecOrMat{Tb}) =
|
||||
_Aq_ldiv_B!(X, lu, B, UMFPACK_Aat)
|
||||
Ac_ldiv_B!{Tb<:Complex}(X::StridedVecOrMat{Tb}, lu::UmfpackLU{Float64}, B::StridedVecOrMat{Tb}) =
|
||||
_Aq_ldiv_B!(X, lu, B, UMFPACK_At)
|
||||
|
||||
function _Aq_ldiv_B!(X::StridedVecOrMat, lu::UmfpackLU, B::StridedVecOrMat, transposeoptype)
|
||||
if size(X, 2) != size(B, 2)
|
||||
throw(DimensionMismatch("input and output arrays must have same number of columns"))
|
||||
end
|
||||
_AqldivB_kernel!(X, lu, B, transposeoptype)
|
||||
return X
|
||||
end
|
||||
function _AqldivB_kernel!{T<:UMFVTypes}(x::StridedVector{T}, lu::UmfpackLU{T},
|
||||
b::StridedVector{T}, transposeoptype)
|
||||
solve!(x, lu, b, transposeoptype)
|
||||
end
|
||||
function _AqldivB_kernel!{T<:UMFVTypes}(X::StridedMatrix{T}, lu::UmfpackLU{T},
|
||||
B::StridedMatrix{T}, transposeoptype)
|
||||
for col in 1:size(X, 2)
|
||||
solve!(view(X, :, col), lu, view(B, :, col), transposeoptype)
|
||||
end
|
||||
end
|
||||
function _AqldivB_kernel!{Tb<:Complex}(x::StridedVector{Tb}, lu::UmfpackLU{Float64},
|
||||
b::StridedVector{Tb}, transposeoptype)
|
||||
r, i = similar(b, Float64), similar(b, Float64)
|
||||
solve!(r, lu, Vector{Float64}(real(b)), transposeoptype)
|
||||
solve!(i, lu, Vector{Float64}(imag(b)), transposeoptype)
|
||||
map!(complex, x, r, i)
|
||||
end
|
||||
function _AqldivB_kernel!{Tb<:Complex}(X::StridedMatrix{Tb}, lu::UmfpackLU{Float64},
|
||||
B::StridedMatrix{Tb}, transposeoptype)
|
||||
r = similar(B, Float64, size(B, 1))
|
||||
i = similar(B, Float64, size(B, 1))
|
||||
for j in 1:size(B, 2)
|
||||
solve!(r, lu, Vector{Float64}(real(view(B, :, j))), transposeoptype)
|
||||
solve!(i, lu, Vector{Float64}(imag(view(B, :, j))), transposeoptype)
|
||||
map!(complex, view(X, :, j), r, i)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function getindex(lu::UmfpackLU, d::Symbol)
|
||||
L,U,p,q,Rs = umf_extract(lu)
|
||||
if d == :L
|
||||
return L
|
||||
elseif d == :U
|
||||
return U
|
||||
elseif d == :p
|
||||
return p
|
||||
elseif d == :q
|
||||
return q
|
||||
elseif d == :Rs
|
||||
return Rs
|
||||
elseif d == :(:)
|
||||
return (L,U,p,q,Rs)
|
||||
else
|
||||
throw(KeyError(d))
|
||||
end
|
||||
end
|
||||
|
||||
for Tv in (:Float64, :Complex128), Ti in UmfpackIndexTypes
|
||||
f = Symbol(umf_nm("free_symbolic", Tv, Ti))
|
||||
@eval begin
|
||||
function ($f)(symb::Ptr{Void})
|
||||
tmp = [symb]
|
||||
ccall(($(string(f)), :libumfpack), Void, (Ptr{Void},), tmp)
|
||||
end
|
||||
|
||||
function umfpack_free_symbolic(lu::UmfpackLU{$Tv,$Ti})
|
||||
if lu.symbolic == C_NULL return lu end
|
||||
umfpack_free_numeric(lu)
|
||||
($f)(lu.symbolic)
|
||||
lu.symbolic = C_NULL
|
||||
return lu
|
||||
end
|
||||
end
|
||||
|
||||
f = Symbol(umf_nm("free_numeric", Tv, Ti))
|
||||
@eval begin
|
||||
function ($f)(num::Ptr{Void})
|
||||
tmp = [num]
|
||||
ccall(($(string(f)), :libumfpack), Void, (Ptr{Void},), tmp)
|
||||
end
|
||||
function umfpack_free_numeric(lu::UmfpackLU{$Tv,$Ti})
|
||||
if lu.numeric == C_NULL return lu end
|
||||
($f)(lu.numeric)
|
||||
lu.numeric = C_NULL
|
||||
return lu
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function umfpack_report_symbolic(symb::Ptr{Void}, level::Real)
|
||||
old_prl::Float64 = umf_ctrl[UMFPACK_PRL]
|
||||
umf_ctrl[UMFPACK_PRL] = Float64(level)
|
||||
@isok ccall((:umfpack_dl_report_symbolic, :libumfpack), Int,
|
||||
(Ptr{Void}, Ptr{Float64}), symb, umf_ctrl)
|
||||
umf_ctrl[UMFPACK_PRL] = old_prl
|
||||
end
|
||||
|
||||
umfpack_report_symbolic(symb::Ptr{Void}) = umfpack_report_symbolic(symb, 4.)
|
||||
|
||||
function umfpack_report_symbolic(lu::UmfpackLU, level::Real)
|
||||
umfpack_report_symbolic(umfpack_symbolic!(lu).symbolic, level)
|
||||
end
|
||||
|
||||
umfpack_report_symbolic(lu::UmfpackLU) = umfpack_report_symbolic(lu.symbolic,4.)
|
||||
function umfpack_report_numeric(num::Ptr{Void}, level::Real)
|
||||
old_prl::Float64 = umf_ctrl[UMFPACK_PRL]
|
||||
umf_ctrl[UMFPACK_PRL] = Float64(level)
|
||||
@isok ccall((:umfpack_dl_report_numeric, :libumfpack), Int,
|
||||
(Ptr{Void}, Ptr{Float64}), num, umf_ctrl)
|
||||
umf_ctrl[UMFPACK_PRL] = old_prl
|
||||
end
|
||||
|
||||
umfpack_report_numeric(num::Ptr{Void}) = umfpack_report_numeric(num, 4.)
|
||||
function umfpack_report_numeric(lu::UmfpackLU, level::Real)
|
||||
umfpack_report_numeric(umfpack_numeric!(lu).numeric, level)
|
||||
end
|
||||
|
||||
umfpack_report_numeric(lu::UmfpackLU) = umfpack_report_numeric(lu,4.)
|
||||
|
||||
end # UMFPACK module
|
||||
@@ -0,0 +1,43 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
## UMFPACK
|
||||
|
||||
## Type of solve
|
||||
const UMFPACK_A = 0 # Ax=b
|
||||
const UMFPACK_At = 1 # A'x=b
|
||||
const UMFPACK_Aat = 2 # A.'x=b
|
||||
const UMFPACK_Pt_L = 3 # P'Lx=b
|
||||
const UMFPACK_L = 4 # Lx=b
|
||||
const UMFPACK_Lt_P = 5 # L'Px=b
|
||||
const UMFPACK_Lat_P = 6 # L.'Px=b
|
||||
const UMFPACK_Lt = 7 # L'x=b
|
||||
const UMFPACK_Lat = 8 # L.'x=b
|
||||
const UMFPACK_U_Qt = 9 # UQ'x=b
|
||||
const UMFPACK_U = 10 # Ux=b
|
||||
const UMFPACK_Q_Ut = 11 # QU'x=b
|
||||
const UMFPACK_Q_Uat = 12 # QU.'x=b
|
||||
const UMFPACK_Ut = 13 # U'x=b
|
||||
const UMFPACK_Uat = 14 # U.'x=b
|
||||
|
||||
## Sizes of Control and Info arrays for returning information from solver
|
||||
const UMFPACK_INFO = 90
|
||||
const UMFPACK_CONTROL = 20
|
||||
const UMFPACK_PRL = 1
|
||||
|
||||
## Status codes
|
||||
const UMFPACK_OK = 0
|
||||
const UMFPACK_WARNING_singular_matrix = 1
|
||||
const UMFPACK_WARNING_determinant_underflow = 2
|
||||
const UMFPACK_WARNING_determinant_overflow = 3
|
||||
const UMFPACK_ERROR_out_of_memory = -1
|
||||
const UMFPACK_ERROR_invalid_Numeric_object = -3
|
||||
const UMFPACK_ERROR_invalid_Symbolic_object = -4
|
||||
const UMFPACK_ERROR_argument_missing = -5
|
||||
const UMFPACK_ERROR_n_nonpositive = -6
|
||||
const UMFPACK_ERROR_invalid_matrix = -8
|
||||
const UMFPACK_ERROR_different_pattern = -11
|
||||
const UMFPACK_ERROR_invalid_system = -13
|
||||
const UMFPACK_ERROR_invalid_permutation = -15
|
||||
const UMFPACK_ERROR_internal_error = -911
|
||||
const UMFPACK_ERROR_file_IO = -17
|
||||
const UMFPACK_ERROR_ordering_failed = -18
|
||||
Reference in New Issue
Block a user