Add: julia-0.6.2

Former-commit-id: ccc667cf67d569f3fb3df39aa57c2134755a7551
This commit is contained in:
2018-02-10 10:27:19 -07:00
parent 94220957d7
commit 019f8e3064
723 changed files with 276164 additions and 0 deletions
@@ -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
+184
View File
@@ -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