fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
This commit is contained in:
925
julia-0.6.3/share/julia/base/sparse/linalg.jl
Normal file
925
julia-0.6.3/share/julia/base/sparse/linalg.jl
Normal file
@@ -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.")
|
||||
Reference in New Issue
Block a user