@@ -3,7 +3,7 @@ module CHOLMOD
33import Base: (* ), convert, copy, eltype, getindex, show, size
44
55import Base. LinAlg: (\ ), A_mul_Bc, A_mul_Bt, Ac_ldiv_B, Ac_mul_B, At_ldiv_B, At_mul_B,
6- cholfact, cholfact!, det, diag, ishermitian, isposdef,
6+ cholfact, det, diag, ishermitian, isposdef,
77 issym, ldltfact, logdet
88
99import Base. SparseMatrix: sparse
5454# Type definitions #
5555# ###################
5656
57- # The three core data types for CHOLMOD: Dense, Sparse and Factor. CHOLMOD
58- # manages the memory, so the Julia versions only wrap a pointer to a struct.
59- # Therefore finalizers should be registret each time a pointer is returned from
60- # CHOLMOD.
57+ # The three core data types for CHOLMOD: Dense, Sparse and Factor.
58+ # CHOLMOD manages the memory, so the Julia versions only wrap a
59+ # pointer to a struct. Therefore finalizers should be registered each
60+ # time a pointer is returned from CHOLMOD.
6161
6262# Dense
6363immutable C_Dense{T<: VTypes }
@@ -195,6 +195,28 @@ type Factor{Tv,Ti} <: Factorization{Tv}
195195 p:: Ptr{C_Factor{Tv,Ti}}
196196end
197197
198+ # FactorComponent, for encoding particular factors from a factorization
199+ type FactorComponent{Tv,Ti,S} <: AbstractMatrix{Tv}
200+ F:: Factor{Tv,Ti}
201+
202+ function FactorComponent (F:: Factor{Tv,Ti} )
203+ s = unsafe_load (F. p)
204+ if bool (s. is_ll)
205+ S == :L || S == :U || S == :PL || S == :UPt || throw (CHOLMODException (S, " not supported for sparse LLt matrices; try :L, :U, :PL, or :UPt" ))
206+ else
207+ S == :L || S == :U || S == :PL || S == :UPt ||
208+ S == :D || S == :LD || S == :DU || S == :PLD || S == :DUPt ||
209+ throw (CHOLMODException (S, " not supported for sparse LDLt matrices; try :L, :U, :PL, :UPt, :D, :LD, :DU, :PLD, or :DUPt" ))
210+ end
211+ new (F)
212+ end
213+ end
214+ function FactorComponent {Tv,Ti} (F:: Factor{Tv,Ti} , sym:: Symbol )
215+ FactorComponent {Tv,Ti,sym} (F)
216+ end
217+
218+ Factor (FC:: FactorComponent ) = Factor (FC. F)
219+
198220# ################
199221# Thin wrappers #
200222# ################
@@ -400,7 +422,7 @@ for Ti in IndexTypes
400422 s
401423 end
402424
403- function transpose {Tv<:VTypes} (A:: Sparse{Tv,$Ti} , values:: Integer )
425+ function transpose_ {Tv<:VTypes} (A:: Sparse{Tv,$Ti} , values:: Integer )
404426 s = Sparse (ccall ((@cholmod_name (" transpose" , $ Ti),:libcholmod ), Ptr{C_Sparse{Tv,$ Ti}},
405427 (Ptr{C_Sparse{Tv,$ Ti}}, Cint, Ptr{UInt8}),
406428 A. p, values, common ($ Ti)))
@@ -550,6 +572,25 @@ for Ti in IndexTypes
550572 finalizer (f, free!)
551573 f
552574 end
575+ function analyze_p {Tv<:VTypes} (A:: Sparse{Tv,$Ti} , perm:: Vector{$Ti} , fset:: Vector{$Ti} , cmmn:: Vector{UInt8} )
576+ f = Factor (ccall ((@cholmod_name (" analyze_p" , $ Ti),:libcholmod ),
577+ Ptr{C_Factor{Tv,$ Ti}},
578+ (Ptr{C_Sparse{Tv,$ Ti}}, Ptr{$ Ti}, Ptr{$ Ti}, Csize_t, Ptr{UInt8}),
579+ A. p, perm, fset, length (fset), cmmn))
580+ finalizer (f, free!)
581+ f
582+ end
583+ function analyze_p2 {Tv<:VTypes} (forchol:: Bool , A:: Sparse{Tv,$Ti} , perm:: Vector{$Ti} , fset:: Vector{$Ti} , cmmn:: Vector{UInt8} )
584+ sA = unsafe_load (A. p)
585+ length (perm) == sA. nrow || throw (ArgumentError (" permutation is of the wrong size" ))
586+
587+ f = Factor (ccall ((@cholmod_name (" analyze_p2" , $ Ti),:libcholmod ),
588+ Ptr{C_Factor{Tv,$ Ti}},
589+ (Cint, Ptr{C_Sparse{Tv,$ Ti}}, Ptr{$ Ti}, Ptr{$ Ti}, Csize_t, Ptr{UInt8}),
590+ forchol, A. p, perm, fset, length (fset), cmmn))
591+ finalizer (f, free!)
592+ f
593+ end
553594 function factorize! {Tv<:VTypes} (A:: Sparse{Tv,$Ti} , F:: Factor{Tv,$Ti} , cmmn:: Vector{UInt8} )
554595 @isok ccall ((@cholmod_name (" factorize" , $ Ti),:libcholmod ), Cint,
555596 (Ptr{C_Sparse{Tv,$ Ti}}, Ptr{C_Factor{Tv,$ Ti}}, Ptr{UInt8}),
@@ -576,7 +617,7 @@ for Ti in IndexTypes
576617 d
577618 end
578619
579- function spsolve {Tv<:VTypes} (sys:: Integer , F:: Factor{Tv,$Ti} , B:: Sparse{Tv,$Ti} )
620+ function solve {Tv<:VTypes} (sys:: Integer , F:: Factor{Tv,$Ti} , B:: Sparse{Tv,$Ti} )
580621 if size (F,1 ) != size (B,1 )
581622 throw (DimensionMismatch (" LHS and RHS should have the same number of rows. LHS has $(size (F,1 )) rows, but RHS has $(size (B,1 )) rows." ))
582623 end
@@ -602,6 +643,13 @@ for Ti in IndexTypes
602643 end
603644end
604645
646+ function get_perm (F:: Factor )
647+ s = unsafe_load (F. p)
648+ p = pointer_to_array (s. Perm, s. n, false )
649+ p+ 1
650+ end
651+ get_perm (FC:: FactorComponent ) = get_perm (Factor (FC))
652+
605653# ########################
606654# High level interfaces #
607655# ########################
@@ -790,9 +838,33 @@ function sparse{Ti}(A::Sparse{Complex{Float64},Ti}) # Notice! Cannot be type sta
790838 end
791839 return convert (Hermitian{Complex{Float64},SparseMatrixCSC{Complex{Float64},Ti}}, A)
792840end
793- sparse (L:: Factor ) = sparse (Sparse (L))
841+ function sparse (F:: Factor )
842+ s = unsafe_load (F. p)
843+ if bool (s. is_ll)
844+ L = Sparse (F)
845+ A = sparse (L* L' )
846+ else
847+ LD = sparse (F[:LD ])
848+ L, d = getLd! (LD)
849+ A = scale (L, d)* L'
850+ end
851+ p = get_perm (F)
852+ if p != [1 : s. n;]
853+ A = A[p,p]
854+ end
855+ A
856+ end
857+
794858sparse (D:: Dense ) = sparse (Sparse (D))
795859
860+ function sparse {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:L} )
861+ F = Factor (FC)
862+ s = unsafe_load (F. p)
863+ bool (s. is_ll) || throw (CHOLMODException (" sparse: supported only for :LD on LDLt factorizations" ))
864+ sparse (Sparse (F))
865+ end
866+ sparse {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:LD} ) = sparse (Sparse (Factor (FC)))
867+
796868# Calculate the offset into the stype field of the cholmod_sparse_struct and
797869# change the value
798870let offidx= findfirst (fieldnames (C_Sparse) .== :stype )
@@ -845,6 +917,19 @@ function size(F::Factor, i::Integer)
845917 return 1
846918end
847919
920+ size (FC:: FactorComponent , i:: Integer ) = size (FC. F, i)
921+ size (FC:: FactorComponent ) = size (FC. F)
922+
923+ ctranspose {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:L} ) = FactorComponent {Tv,Ti,:U} (FC. F)
924+ ctranspose {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:U} ) = FactorComponent {Tv,Ti,:L} (FC. F)
925+ ctranspose {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:PL} ) = FactorComponent {Tv,Ti,:UPt} (FC. F)
926+ ctranspose {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:UPt} ) = FactorComponent {Tv,Ti,:PL} (FC. F)
927+ ctranspose {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:D} ) = FC
928+ ctranspose {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:LD} ) = FactorComponent {Tv,Ti,:DU} (FC. F)
929+ ctranspose {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:DU} ) = FactorComponent {Tv,Ti,:LD} (FC. F)
930+ ctranspose {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:PLD} ) = FactorComponent {Tv,Ti,:DUPt} (FC. F)
931+ ctranspose {Tv,Ti} (FC:: FactorComponent{Tv,Ti,:DUPt} ) = FactorComponent {Tv,Ti,:PLD} (FC. F)
932+
848933function getindex (A:: Dense , i:: Integer )
849934 s = unsafe_load (A. p)
850935 0 < i <= s. nrow* s. ncol || throw (BoundsError ())
@@ -871,6 +956,27 @@ function getindex{T}(A::Sparse{T}, i0::Integer, i1::Integer)
871956 ((r1 > r2) || (unsafe_load (s. i, r1) + 1 != i0)) ? zero (T) : unsafe_load (s. x, r1)
872957end
873958
959+ function getindex (F:: Factor , sym:: Symbol )
960+ sym == :p && return get_perm (F)
961+ FactorComponent (F, sym)
962+ end
963+
964+ function getLd! (S:: SparseMatrixCSC )
965+ d = Array (eltype (S), size (S, 1 ))
966+ fill! (d, 0 )
967+ col = 1
968+ for k = 1 : length (S. nzval)
969+ while k >= S. colptr[col+ 1 ]
970+ col += 1
971+ end
972+ if S. rowval[k] == col
973+ d[col] = S. nzval[k]
974+ S. nzval[k] = 1
975+ end
976+ end
977+ S, d
978+ end
979+
874980# # Multiplication
875981(* )(A:: Sparse , B:: Sparse ) = ssmult (A, B, 0 , true , true )
876982(* )(A:: Sparse , B:: Dense ) = sdmult! (A, false , 1. , 0. , B, zeros (size (A, 1 ), size (B, 2 )))
@@ -880,7 +986,7 @@ function A_mul_Bc{Tv<:VRealTypes,Ti<:ITypes}(A::Sparse{Tv,Ti}, B::Sparse{Tv,Ti})
880986 cm = common (Ti)
881987
882988 if ! is (A,B)
883- aa1 = transpose (B, 2 )
989+ aa1 = transpose_ (B, 2 )
884990 # # result of ssmult will have stype==0, contain numerical values and be sorted
885991 return ssmult (A, aa1, 0 , true , true )
886992 end
@@ -898,7 +1004,7 @@ function A_mul_Bc{Tv<:VRealTypes,Ti<:ITypes}(A::Sparse{Tv,Ti}, B::Sparse{Tv,Ti})
8981004end
8991005
9001006function Ac_mul_B (A:: Sparse , B:: Sparse )
901- aa1 = transpose (A, 2 )
1007+ aa1 = transpose_ (A, 2 )
9021008 if is (A,B)
9031009 return A_mul_Bc (aa1, aa1)
9041010 end
@@ -943,6 +1049,25 @@ function cholfact{Tv<:VTypes,Ti<:ITypes}(A::Sparse{Tv,Ti}, β::Tv)
9431049 return F
9441050end
9451051
1052+ function cholfact {Tv<:VTypes,Ti<:ITypes,Tii<:Integer} (A:: Sparse{Tv,Ti} , perm:: Vector{Tii} )
1053+ sA = unsafe_load (A. p)
1054+ sA. stype == 0 && throw (ArgumentError (" sparse matrix is not symmetric/Hermitian" ))
1055+ isperm (perm) || throw (ArgumentError (" perm is not a permutation" ))
1056+
1057+ cm = common (Ti)
1058+
1059+ # Hack! makes it a llt
1060+ cm[common_final_ll] = reinterpret (UInt8, [one (Cint)])
1061+
1062+ F = analyze_p2 (true , A, convert (Vector{Ti}, perm- 1 ), convert (Vector{Ti}, [0 : sA. ncol- 1 ;]), cm)
1063+ factorize! (A, F, cm)
1064+
1065+ s = unsafe_load (F. p)
1066+ s. minor < size (A, 1 ) && throw (Base. LinAlg. PosDefException (s. minor))
1067+ return F
1068+ end
1069+ cholfact (A:: SparseMatrixCSC , perm) = cholfact (Sparse (A), perm)
1070+
9461071function ldltfact (A:: Sparse )
9471072 sA = unsafe_load (A. p)
9481073 sA. stype == 0 && throw (ArgumentError (" sparse matrix is not symmetric/Hermitian" ))
@@ -987,6 +1112,30 @@ function ldltfact{Tv<:VTypes,Ti<:ITypes}(A::Sparse{Tv,Ti}, β::Tv)
9871112 return F
9881113end
9891114
1115+ function ldltfact {Tv<:VTypes,Ti<:ITypes,Tii<:Integer} (A:: Sparse{Tv,Ti} , perm:: Vector{Tii} )
1116+ sA = unsafe_load (A. p)
1117+ sA. stype == 0 && throw (ArgumentError (" sparse matrix is not symmetric/Hermitian" ))
1118+ isperm (perm) || throw (ArgumentError (" perm is not a permutation" ))
1119+
1120+ cm = common (indtype (A))
1121+
1122+ # Hack! makes it a ldlt
1123+ cm[common_final_ll] = reinterpret (UInt8, [zero (Cint)])
1124+
1125+ # Hack! really make sure it's a ldlt by avoiding supernodal factorisation
1126+ cm[common_supernodal] = reinterpret (UInt8, [zero (Cint)])
1127+
1128+ F = analyze_p2 (true , A, convert (Vector{Ti}, perm- 1 ), convert (Vector{Ti}, [0 : sA. ncol- 1 ;]), cm)
1129+ factorize! (A, F, cm)
1130+
1131+ # Check if decomposition failed
1132+ s = unsafe_load (F. p)
1133+ s. minor < size (A, 1 ) && throw (Base. LinAlg. ArgumentError (" matrix has one or more zero pivots" ))
1134+
1135+ return F
1136+ end
1137+ ldltfact (A:: SparseMatrixCSC , perm) = ldltfact (Sparse (A), perm)
1138+
9901139cholfact {T<:VTypes} (A:: Sparse{T} , β:: Number ) = cholfact (A, convert (T, β))
9911140cholfact (A:: SparseMatrixCSC ) = cholfact (Sparse (A))
9921141cholfact (A:: SparseMatrixCSC , β:: Number ) = cholfact (Sparse (A), β)
@@ -1024,16 +1173,63 @@ update!{T<:VTypes}(F::Factor{T}, A::SparseMatrixCSC{T}, β::Number) = update!(F,
10241173
10251174# # Solvers
10261175
1176+ # Solve Lx = b and L'x=b where A = L*L'
1177+ function (\ ){T,Ti}(L:: FactorComponent{T,Ti,:L} , B:: Union(Dense,Sparse) )
1178+ solve (CHOLMOD_L, Factor (L), B)
1179+ end
1180+ function (\ ){T,Ti}(L:: FactorComponent{T,Ti,:U} , B:: Union(Dense,Sparse) )
1181+ solve (CHOLMOD_Lt, Factor (L), B)
1182+ end
1183+ # Solve PLx = b and L'P'x=b where A = P*L*L'*P'
1184+ function (\ ){T,Ti}(L:: FactorComponent{T,Ti,:PL} , B:: Union(Dense,Sparse) )
1185+ F = Factor (L)
1186+ solve (CHOLMOD_L, F, solve (CHOLMOD_Pt, F, B)) # Confusingly, CHOLMOD_Pt solves Px = b
1187+ end
1188+ function (\ ){T,Ti}(L:: FactorComponent{T,Ti,:UPt} , B:: Union(Dense,Sparse) )
1189+ F = Factor (L)
1190+ solve (CHOLMOD_P, F, solve (CHOLMOD_Lt, F, B))
1191+ end
1192+ # Solve various equations for A = L*D*L' and A = P*L*D*L'*P'
1193+ function (\ ){T,Ti}(L:: FactorComponent{T,Ti,:D} , B:: Union(Dense,Sparse) )
1194+ solve (CHOLMOD_D, Factor (L), B)
1195+ end
1196+ function (\ ){T,Ti}(L:: FactorComponent{T,Ti,:LD} , B:: Union(Dense,Sparse) )
1197+ solve (CHOLMOD_LD, Factor (L), B)
1198+ end
1199+ function (\ ){T,Ti}(L:: FactorComponent{T,Ti,:DU} , B:: Union(Dense,Sparse) )
1200+ solve (CHOLMOD_DLt, Factor (L), B)
1201+ end
1202+ function (\ ){T,Ti}(L:: FactorComponent{T,Ti,:PLD} , B:: Union(Dense,Sparse) )
1203+ F = Factor (L)
1204+ solve (CHOLMOD_LD, F, solve (CHOLMOD_Pt, F, B))
1205+ end
1206+ function (\ ){T,Ti}(L:: FactorComponent{T,Ti,:DUPt} , B:: Union(Dense,Sparse) )
1207+ F = Factor (L)
1208+ solve (CHOLMOD_P, F, solve (CHOLMOD_DLt, F, B))
1209+ end
1210+
1211+ function (\ )(L:: FactorComponent , b:: Vector )
1212+ reshape (convert (Matrix, L\ Dense (b)), length (b))
1213+ end
1214+ function (\ )(L:: FactorComponent , B:: Matrix )
1215+ convert (Matrix, L\ Dense (B))
1216+ end
1217+ function (\ )(L:: FactorComponent , B:: SparseMatrixCSC )
1218+ sparse (L\ Sparse (B,0 ))
1219+ end
1220+
1221+ Ac_ldiv_B (L:: FactorComponent , B) = ctranspose (L)\ B
1222+
10271223(\ )(L:: Factor , B:: Dense ) = solve (CHOLMOD_A, L, B)
10281224(\ )(L:: Factor , b:: Vector ) = reshape (convert (Matrix, solve (CHOLMOD_A, L, Dense (b))), length (b))
10291225(\ )(L:: Factor , B:: Matrix ) = convert (Matrix, solve (CHOLMOD_A, L, Dense (B)))
1030- (\ )(L:: Factor , B:: Sparse ) = spsolve (CHOLMOD_A, L, B)
1226+ (\ )(L:: Factor , B:: Sparse ) = solve (CHOLMOD_A, L, B)
10311227# When right hand side is sparse, we have to ensure that the rhs is not marked as symmetric.
1032- (\ )(L:: Factor , B:: SparseMatrixCSC ) = sparse (spsolve (CHOLMOD_A, L, Sparse (B, 0 )))
1228+ (\ )(L:: Factor , B:: SparseMatrixCSC ) = sparse (solve (CHOLMOD_A, L, Sparse (B, 0 )))
10331229
10341230Ac_ldiv_B (L:: Factor , B:: Dense ) = solve (CHOLMOD_A, L, B)
10351231Ac_ldiv_B (L:: Factor , B:: VecOrMat ) = convert (Matrix, solve (CHOLMOD_A, L, Dense (B)))
1036- Ac_ldiv_B (L:: Factor , B:: Sparse ) = spsolve (CHOLMOD_A, L, B)
1232+ Ac_ldiv_B (L:: Factor , B:: Sparse ) = solve (CHOLMOD_A, L, B)
10371233Ac_ldiv_B (L:: Factor , B:: SparseMatrixCSC ) = Ac_ldiv_B (L, Sparse (B))
10381234
10391235# # Other convenience methods
0 commit comments