@@ -258,8 +258,16 @@ function Sparse(p::Ptr{cholmod_sparse})
258258 Sparse {jlxtype(s.xtype, s.dtype)} (p)
259259end
260260
261+ # Factor stores its own temporary CHOLMOD Y/E buffers for use in ldiv!
262+ # and pre-allocates cholmod_dense_struct wrappers
261263mutable struct Factor{Tv<: VTypes , Ti<: ITypes } <: Factorization{Tv}
262264 ptr:: Ptr{cholmod_factor}
265+ dense_x:: cholmod_dense_struct
266+ dense_b:: cholmod_dense_struct
267+ X:: Ref{Ptr{cholmod_dense_struct}}
268+ Y:: Ref{Ptr{cholmod_dense_struct}}
269+ E:: Ref{Ptr{cholmod_dense_struct}}
270+ lock:: ReentrantLock
263271 function Factor {Tv, Ti} (ptr:: Ptr{cholmod_factor} , register_finalizer = true ) where {Tv, Ti}
264272 if ptr == C_NULL
265273 throw (ArgumentError (" factorization construction failed for " *
@@ -276,9 +284,15 @@ mutable struct Factor{Tv<:VTypes, Ti<:ITypes} <: Factorization{Tv}
276284 free! (ptr, Ti)
277285 throw (CHOLMODException (" dtype=$(dtyp (Tv)) not supported" ))
278286 end
279- F = new (ptr)
287+ F = new (ptr,
288+ cholmod_dense_struct (),
289+ cholmod_dense_struct (),
290+ Ref (Ptr {cholmod_dense_struct} (C_NULL )),
291+ Ref (Ptr {cholmod_dense_struct} (C_NULL )),
292+ Ref (Ptr {cholmod_dense_struct} (C_NULL )),
293+ ReentrantLock ())
280294 if register_finalizer
281- finalizer (free!, F)
295+ finalizer (free!, F) # includes Y/E buffers
282296 end
283297 return F
284298 end
@@ -857,32 +871,6 @@ function Base.convert(::Type{Dense{Tnew}}, A::Dense{T}) where {Tnew, T}
857871end
858872Base. convert (:: Type{Dense{T}} , A:: Dense{T} ) where T = A
859873
860- # Just calling Dense(x) or Dense(b) will allocate new
861- # `cholmod_dense_struct`s in CHOLMOD. Instead, we want to reuse
862- # the existing memory. We can do this by creating new
863- # `cholmod_dense_struct`s and filling them manually.
864- function wrap_dense_and_ptr (x:: StridedVecOrMat{T} ) where {T <: VTypes }
865- dense_x = cholmod_dense_struct ()
866- dense_x. nrow = size (x, 1 )
867- dense_x. ncol = size (x, 2 )
868- dense_x. nzmax = length (x)
869- dense_x. d = stride (x, 2 )
870- dense_x. x = pointer (x)
871- dense_x. z = C_NULL
872- dense_x. xtype = xtyp (eltype (x))
873- dense_x. dtype = dtyp (eltype (x))
874- return dense_x, pointer_from_objref (dense_x)
875- end
876- # We need to use a special handling for the case of `Dense`
877- # input arrays since the `pointer` refers to the pointer to the
878- # `cholmod_dense`, not to the array values themselves as for
879- # standard arrays.
880- function wrap_dense_and_ptr (x:: Dense{T} ) where {T <: VTypes }
881- dense_x_ptr = x. ptr
882- dense_x = unsafe_load (dense_x_ptr)
883- return dense_x, pointer_from_objref (dense_x)
884- end
885-
886874# This constructor assumes zero based colptr and rowval
887875function Sparse (m:: Integer , n:: Integer ,
888876 colptr0:: Vector{Ti} , rowval0:: Vector{Ti} ,
@@ -1240,7 +1228,11 @@ end
12401228
12411229free! (A:: Dense ) = free! (pointer (A))
12421230free! (A:: Sparse{<:Any, Ti} ) where Ti = free! (pointer (A), Ti)
1243- free! (F:: Factor{<:Any, Ti} ) where Ti = free! (pointer (F), Ti)
1231+ function free! (F:: Factor{<:Any, Ti} ) where Ti
1232+ free! (getfield (F, :Y )[])
1233+ free! (getfield (F, :E )[])
1234+ free! (pointer (F), Ti)
1235+ end
12441236
12451237nnz (F:: Factor ) = nnz (Sparse (F))
12461238
@@ -1325,8 +1317,8 @@ end
13251317@inline function getproperty (F:: Factor , sym:: Symbol )
13261318 if sym === :p
13271319 return get_perm (F)
1328- elseif sym === :ptr
1329- return getfield (F, :ptr )
1320+ elseif sym === :ptr || sym === :lock
1321+ return getfield (F, sym )
13301322 else
13311323 return FactorComponent (F, sym)
13321324 end
@@ -1433,11 +1425,11 @@ end
14331425
14341426function cholesky! (F:: Factor{Tv} , A:: Sparse{Tv} ;
14351427 shift:: Real = 0.0 , check:: Bool = true ) where Tv
1436- # Compute the numerical factorization
1437- @cholmod_param final_ll = true begin
1438- factorize_p! (A, shift, F)
1428+ @lock F. lock begin
1429+ @cholmod_param final_ll = true begin
1430+ factorize_p! (A, shift, F)
1431+ end
14391432 end
1440-
14411433 check && (issuccess (F) || throw (LinearAlgebra. PosDefException (1 )))
14421434 return F
14431435end
@@ -1608,12 +1600,10 @@ LinearAlgebra._cholesky(A::Union{SparseMatrixCSC{T}, SparseMatrixCSC{Complex{T}}
16081600
16091601function ldlt! (F:: Factor{Tv} , A:: Sparse{Tv} ;
16101602 shift:: Real = 0.0 , check:: Bool = true ) where Tv
1611- # Makes it an LDLt
1612- change_factor! (F, false , false , true , false )
1613-
1614- # Compute the numerical factorization
1615- factorize_p! (A, shift, F)
1616-
1603+ @lock F. lock begin
1604+ change_factor! (F, false , false , true , false )
1605+ factorize_p! (A, shift, F)
1606+ end
16171607 check && (issuccess (F) || throw (LinearAlgebra. ZeroPivotException (1 )))
16181608 return F
16191609end
@@ -1915,8 +1905,52 @@ const AbstractSparseVecOrMatInclAdjAndTrans = Union{AbstractSparseVecOrMat, AdjO
19151905 throw (ArgumentError (" self-adjoint sparse system solve not implemented for sparse rhs B," *
19161906 " consider to convert B to a dense array" ))
19171907
1918- # in-place ldiv!
1908+ # Julia array -> fill dense_b fields and return pointer to it
1909+ # CHOLMOD Dense object -> struct is already set up by CHOLMOD, return b.ptr directly
1910+ @inline function _setup_bptr (b:: StridedVecOrMat{T} , dense_b:: cholmod_dense_struct ) where T<: VTypes
1911+ dense_b. nrow = size (b, 1 )
1912+ dense_b. ncol = size (b, 2 )
1913+ dense_b. nzmax = length (b)
1914+ dense_b. d = stride (b, 2 )
1915+ dense_b. x = pointer (b)
1916+ dense_b. z = C_NULL
1917+ dense_b. xtype = xtyp (T)
1918+ dense_b. dtype = dtyp (T)
1919+ return Ptr {cholmod_dense_struct} (pointer_from_objref (dense_b))
1920+ end
1921+ @inline _setup_bptr (b:: Dense{<:VTypes} , :: cholmod_dense_struct ) = b. ptr
1922+
19191923for TI in IndexTypes
1924+ @eval function solve! (x:: StridedVecOrMat{T} , L:: Factor{T, $TI} , b:: StridedVecOrMat{T} ) where {T<: VTypes }
1925+ @lock L. lock begin
1926+ dense_x = getfield (L, :dense_x )
1927+ X = getfield (L, :X )
1928+ Y = getfield (L, :Y )
1929+ E = getfield (L, :E )
1930+
1931+ dense_x. nrow = size (x, 1 )
1932+ dense_x. ncol = size (x, 2 )
1933+ dense_x. nzmax = length (x)
1934+ dense_x. d = stride (x, 2 )
1935+ dense_x. x = pointer (x)
1936+ dense_x. z = C_NULL
1937+ dense_x. xtype = xtyp (T)
1938+ dense_x. dtype = dtyp (T)
1939+
1940+ X[] = Ptr {cholmod_dense_struct} (pointer_from_objref (dense_x))
1941+ Bptr = _setup_bptr (b, getfield (L, :dense_b ))
1942+ status = GC. @preserve x b L begin
1943+ $ (cholname (:solve2 , TI))(
1944+ CHOLMOD_A, L,
1945+ Bptr, C_NULL ,
1946+ X, C_NULL ,
1947+ Y, E,
1948+ getcommon ($ TI))
1949+ end
1950+ @assert ! iszero (status)
1951+ end
1952+ end
1953+
19201954 @eval function ldiv! (x:: StridedVecOrMat{T} ,
19211955 L:: Factor{T, $TI} ,
19221956 b:: StridedVecOrMat{T} ) where {T<: VTypes }
@@ -1943,34 +1977,7 @@ for TI in IndexTypes
19431977 throw (LinearAlgebra. ZeroPivotException (s. minor))
19441978 end
19451979 end
1946-
1947- # Just calling Dense(x) or Dense(b) will allocate new
1948- # `cholmod_dense_struct`s in CHOLMOD. Instead, we want to reuse
1949- # the existing memory. We can do this by creating new
1950- # `cholmod_dense_struct`s and filling them manually.
1951- dense_x, dense_x_ptr = wrap_dense_and_ptr (x)
1952- dense_b, dense_b_ptr = wrap_dense_and_ptr (b)
1953-
1954- X_Handle = Ptr {cholmod_dense_struct} (dense_x_ptr)
1955- Y_Handle = Ref (Ptr {cholmod_dense_struct} (C_NULL ))
1956- E_Handle = Ref (Ptr {cholmod_dense_struct} (C_NULL ))
1957- status = GC. @preserve x dense_x b dense_b begin
1958- $ (cholname (:solve2 , TI))(
1959- CHOLMOD_A, L,
1960- Ref (dense_b), C_NULL ,
1961- Ref (X_Handle), C_NULL ,
1962- Y_Handle,
1963- E_Handle,
1964- getcommon ($ TI))
1965- end
1966- if Y_Handle[] != C_NULL
1967- free! (Y_Handle[])
1968- end
1969- if E_Handle[] != C_NULL
1970- free! (E_Handle[])
1971- end
1972- @assert ! iszero (status)
1973-
1980+ solve! (x, L, b)
19741981 return x
19751982 end
19761983end
0 commit comments