@@ -18,7 +18,7 @@ using LinearAlgebra
1818using  LinearAlgebra:  RealHermSymComplexHerm, AdjOrTrans
1919import  LinearAlgebra:  (\ ), AdjointFactorization,
2020                 cholesky, cholesky!, det, diag, ishermitian, isposdef,
21-                  issuccess, issymmetric, ldlt, ldlt!, logdet,
21+                  issuccess, issymmetric, ldiv!,  ldlt, ldlt!, logdet,
2222                 lowrankdowndate, lowrankdowndate!, lowrankupdate, lowrankupdate!
2323
2424using  SparseArrays
@@ -795,7 +795,7 @@ function ssmult(A::Sparse{Tv1, Ti1}, B::Sparse{Tv2, Ti2}, stype::Integer,
795795    A, B =  convert .(Sparse{promote_type (Tv1, Tv2), promote_type (Ti1, Ti2)}, (A, B))
796796    return  ssmult (A, B, stype, values, sorted)
797797end 
798- function  horzcat (A:: Sparse{Tv1, Ti1} , B:: Sparse{Tv2, Ti2} , values:: Bool ) where   
798+ function  horzcat (A:: Sparse{Tv1, Ti1} , B:: Sparse{Tv2, Ti2} , values:: Bool ) where 
799799        {Tv1<: VRealTypes , Tv2<: VRealTypes , Ti1, Ti2}
800800    A, B =  convert .(Sparse{promote_type (Tv1, Tv2), promote_type (Ti1, Ti2)}, (A, B))
801801    return  horzcat (A, B, values)
@@ -809,7 +809,7 @@ function sdmult!(A::Sparse{Tv1, Ti}, transpose::Bool,
809809    A, X =  convert (Sparse{Tv3, Ti}, A), convert (Dense{Tv3}, X)
810810    return  sdmult! (A, transpose, α, β, X, Y)
811811end 
812- function  vertcat (A:: Sparse{Tv1, Ti1} , B:: Sparse{Tv2, Ti2} , values:: Bool ) where   
812+ function  vertcat (A:: Sparse{Tv1, Ti1} , B:: Sparse{Tv2, Ti2} , values:: Bool ) where 
813813        {Tv1<: VRealTypes , Ti1, Tv2<: VRealTypes , Ti2}
814814    A, B =  convert .(Sparse{promote_type (Tv1, Tv2), promote_type (Ti1, Ti2)}, (A, B))
815815    return  vertcat (A, B, values)
@@ -895,7 +895,7 @@ function Dense(A::StridedVecOrMatInclAdjAndTrans)
895895    return  Dense {T} (A)
896896end 
897897#  Don't always promote to Float64 now that we have Float32 support.
898- Dense (A:: StridedVecOrMatInclAdjAndTrans{T} ) where   
898+ Dense (A:: StridedVecOrMatInclAdjAndTrans{T} ) where 
899899    {T<: Union{Float16, ComplexF16, Float32, ComplexF32} } =  Dense {promote_type(T, Float32)} (A)
900900
901901
@@ -913,6 +913,32 @@ function Base.convert(::Type{Dense{Tnew}}, A::Dense{T}) where {Tnew, T}
913913end 
914914Base. convert (:: Type{Dense{T}} , A:: Dense{T} ) where  T =  A
915915
916+ #  Just calling Dense(x) or Dense(b) will allocate new
917+ #  `cholmod_dense_struct`s in CHOLMOD. Instead, we want to reuse
918+ #  the existing memory. We can do this by creating new
919+ #  `cholmod_dense_struct`s and filling them manually.
920+ function  wrap_dense_and_ptr (x:: StridedVecOrMat{T} ) where  {T <:  VTypes }
921+     dense_x =  cholmod_dense_struct ()
922+     dense_x. nrow =  size (x, 1 )
923+     dense_x. ncol =  size (x, 2 )
924+     dense_x. nzmax =  length (x)
925+     dense_x. d =  stride (x, 2 )
926+     dense_x. x =  pointer (x)
927+     dense_x. z =  C_NULL 
928+     dense_x. xtype =  xtyp (eltype (x))
929+     dense_x. dtype =  dtyp (eltype (x))
930+     return  dense_x, pointer_from_objref (dense_x)
931+ end 
932+ #  We need to use a special handling for the case of `Dense`
933+ #  input arrays since the `pointer` refers to the pointer to the
934+ #  `cholmod_dense`, not to the array values themselves as for
935+ #  standard arrays.
936+ function  wrap_dense_and_ptr (x:: Dense{T} ) where  {T <:  VTypes }
937+     dense_x_ptr =  x. ptr
938+     dense_x =  unsafe_load (dense_x_ptr)
939+     return  dense_x, pointer_from_objref (dense_x)
940+ end 
941+ 
916942#  This constructor assumes zero based colptr and rowval
917943function  Sparse (m:: Integer , n:: Integer ,
918944        colptr0:: Vector{Ti} , rowval0:: Vector{Ti} ,
@@ -1055,8 +1081,8 @@ Sparse(A::Hermitian{Tv, SparseMatrixCSC{Tv,Ti}}) where {Tv, Ti} =
10551081    Sparse {promote_type(Tv, Float64), Ti <: ITypes ? Ti : promote_type(Ti, Int)} (
10561082        A. data, A. uplo ==  ' L' ?  - 1  :  1 
10571083    )
1058- Sparse (A:: Hermitian{Tv, SparseMatrixCSC{Tv,Ti}} ) where   
1059-     {Tv<: Union{Float16, Float32, ComplexF32, ComplexF16} , Ti} =   
1084+ Sparse (A:: Hermitian{Tv, SparseMatrixCSC{Tv,Ti}} ) where 
1085+     {Tv<: Union{Float16, Float32, ComplexF32, ComplexF16} , Ti} = 
10601086    Sparse {promote_type(Float32, Tv), Ti <: ITypes ? Ti : promote_type(Ti, Int)} (
10611087        A. data, A. uplo ==  ' L' ?  - 1  :  1 
10621088    )
@@ -1076,7 +1102,7 @@ function Base.convert(::Type{Sparse{Tnew, Inew}}, A::Sparse{Tv, Ti}) where {Tnew
10761102        a =  unsafe_load (typedpointer (A))
10771103        S =  allocate_sparse (a. nrow, a. ncol, a. nzmax, Bool (a. sorted), Bool (a. packed), a. stype, Tnew, Inew)
10781104        s =  unsafe_load (typedpointer (S))
1079-          
1105+ 
10801106        ap =  unsafe_wrap (Array, a. p, (a. ncol +  1 ,), own =  false )
10811107        sp =  unsafe_wrap (Array, s. p, (s. ncol +  1 ,), own =  false )
10821108        copyto! (sp, ap)
@@ -1376,7 +1402,7 @@ end
13761402
13771403# # Multiplication
13781404(* )(A:: Sparse , B:: Sparse ) =  ssmult (A, B, 0 , true , true )
1379- (* )(A:: Sparse , B:: Dense ) =  sdmult! (A, false , 1. , 0. , B,  
1405+ (* )(A:: Sparse , B:: Dense ) =  sdmult! (A, false , 1. , 0. , B,
13801406    zeros (size (A, 1 ), size (B, 2 ), promote_type (eltype (A), eltype (B)))
13811407)
13821408(* )(A:: Sparse , B:: VecOrMat ) =  (* )(A, Dense (B))
@@ -1413,7 +1439,7 @@ function *(adjA::Adjoint{<:Any,<:Sparse}, B::Sparse)
14131439end 
14141440
14151441* (adjA:: Adjoint{<:Any,<:Sparse} , B:: Dense ) =  (
1416-     A =  parent (adjA); sdmult! (A, true , 1. , 0. , B,  
1442+     A =  parent (adjA); sdmult! (A, true , 1. , 0. , B,
14171443    zeros (size (A, 2 ), size (B, 2 ), promote_type (eltype (A), eltype (B))))
14181444)
14191445* (adjA:: Adjoint{<:Any,<:Sparse} , B:: VecOrMat ) =  adjA *  Dense (B)
@@ -1423,25 +1449,33 @@ end
14231449
14241450# # Compute that symbolic factorization only
14251451function  symbolic (A:: Sparse{<:VTypes, Ti} ;
1426-     perm:: Union{Nothing,AbstractVector{<:Integer}} = nothing ,
1427-     postorder:: Bool = isnothing (perm)|| isempty (perm), userperm_only:: Bool = true ) where  Ti
1452+                   perm:: Union{Nothing,AbstractVector{<:Integer}} = nothing ,
1453+                   postorder:: Bool = isnothing (perm)|| isempty (perm),
1454+                   userperm_only:: Bool = true ,
1455+                   nested_dissection:: Bool = false ) where  Ti
14281456
14291457    sA =  unsafe_load (pointer (A))
14301458    sA. stype ==  0  &&  throw (ArgumentError (" sparse matrix is not symmetric/Hermitian" 
14311459
1432-     @cholmod_param  postorder =  postorder begin 
1433-         if  perm ===  nothing  ||  isempty (perm) #  TODO : deprecate empty perm
1434-             return  analyze (A)
1435-         else  #  user permutation provided
1436-             if  userperm_only #  use perm even if it is worse than AMD
1437-                 @cholmod_param  nmethods =  1  begin 
1460+     #  The default is to just use AMD. Use nested dissection only if explicitly asked for.
1461+     #  https://github.com/JuliaSparse/SparseArrays.jl/issues/548
1462+     #  https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/26ababc7f3af725c5fb9168a1b94850eab74b666/CHOLMOD/Include/cholmod.h#L555-L574
1463+     @cholmod_param  nmethods =  (nested_dissection ?  0  :  2 ) begin 
1464+         @cholmod_param  postorder =  postorder begin 
1465+             if  perm ===  nothing  ||  isempty (perm) #  TODO : deprecate empty perm
1466+                 return  analyze (A)
1467+             else  #  user permutation provided
1468+                 if  userperm_only #  use perm even if it is worse than AMD
1469+                     @cholmod_param  nmethods =  1  begin 
1470+                         return  analyze_p (A, Ti[p- 1  for  p in  perm])
1471+                     end 
1472+                 else 
14381473                    return  analyze_p (A, Ti[p- 1  for  p in  perm])
14391474                end 
1440-             else 
1441-                 return  analyze_p (A, Ti[p- 1  for  p in  perm])
14421475            end 
14431476        end 
14441477    end 
1478+ 
14451479end 
14461480
14471481function  cholesky! (F:: Factor{Tv} , A:: Sparse{Tv} ;
@@ -1467,7 +1501,7 @@ See also [`cholesky`](@ref).
14671501
14681502!!! note 
14691503    This method uses the CHOLMOD library from SuiteSparse, which only supports 
1470-     real or complex types in single or double precision.   
1504+     real or complex types in single or double precision. 
14711505    Input matrices not of those element types will 
14721506    be converted to these types as appropriate. 
14731507""" 
@@ -1587,8 +1621,8 @@ true
15871621
15881622!!! note 
15891623    This method uses the CHOLMOD[^ACM887][^DavisHager2009] library from [SuiteSparse](https://github.com/DrTimothyAldenDavis/SuiteSparse). 
1590-     CHOLMOD only supports real or complex types in single or double precision.   
1591-     Input matrices not of those element types will be   
1624+     CHOLMOD only supports real or complex types in single or double precision. 
1625+     Input matrices not of those element types will be 
15921626    converted to these types as appropriate. 
15931627
15941628    Many other functions from CHOLMOD are wrapped but not exported from the 
@@ -1633,8 +1667,8 @@ have the type tag, it must still be symmetric or Hermitian.
16331667See also [`ldlt`](@ref). 
16341668
16351669!!! note 
1636-     This method uses the CHOLMOD library from [SuiteSparse](https://github.com/DrTimothyAldenDavis/SuiteSparse),   
1637-     which only supports real or complex types in single or double precision.   
1670+     This method uses the CHOLMOD library from [SuiteSparse](https://github.com/DrTimothyAldenDavis/SuiteSparse), 
1671+     which only supports real or complex types in single or double precision. 
16381672    Input matrices not of those element types will 
16391673    be converted to these types as appropriate. 
16401674""" 
@@ -1695,7 +1729,7 @@ it should be a permutation of `1:size(A,1)` giving the ordering to use
16951729
16961730!!! note 
16971731    This method uses the CHOLMOD[^ACM887][^DavisHager2009] library from [SuiteSparse](https://github.com/DrTimothyAldenDavis/SuiteSparse). 
1698-     CHOLMOD only supports real or complex types in single or double precision.   
1732+     CHOLMOD only supports real or complex types in single or double precision. 
16991733    Input matrices not of those element types will 
17001734    be converted to these types as appropriate. 
17011735
@@ -1767,7 +1801,7 @@ See also [`lowrankupdate!`](@ref), [`lowrankdowndate`](@ref), [`lowrankdowndate!
17671801""" 
17681802lowrankupdate (F:: Factor{Tv} , V:: AbstractArray{Tv2} ) where  {Tv, Tv2} = 
17691803    lowrankupdate! (
1770-         change_xdtype (F, promote_type (Tv, Tv2)),  
1804+         change_xdtype (F, promote_type (Tv, Tv2)),
17711805        convert (AbstractArray{promote_type (Tv, Tv2)}, V)
17721806    )
17731807
@@ -1782,7 +1816,7 @@ See also [`lowrankdowndate!`](@ref), [`lowrankupdate`](@ref), [`lowrankupdate!`]
17821816""" 
17831817lowrankdowndate (F:: Factor{Tv} , V:: AbstractArray{Tv2} ) where  {Tv, Tv2} = 
17841818lowrankdowndate! (
1785-     change_xdtype (F, promote_type (Tv, Tv2)),  
1819+     change_xdtype (F, promote_type (Tv, Tv2)),
17861820    convert (AbstractArray{promote_type (Tv, Tv2)}, V)
17871821)
17881822
@@ -1905,6 +1939,66 @@ const AbstractSparseVecOrMatInclAdjAndTrans = Union{AbstractSparseVecOrMat, AdjO
19051939    throw (ArgumentError (" self-adjoint sparse system solve not implemented for sparse rhs B," * 
19061940        "  consider to convert B to a dense array" 
19071941
1942+ #  in-place ldiv!
1943+ for  TI in  IndexTypes
1944+     @eval  function  ldiv! (x:: StridedVecOrMat{T} ,
1945+                          L:: Factor{T, $TI} ,
1946+                          b:: StridedVecOrMat{T} ) where  {T<: VTypes }
1947+         if  x ===  b
1948+             throw (ArgumentError (" output array must not be aliased with input array" 
1949+         end 
1950+         if  size (L, 1 ) !=  size (b, 1 )
1951+             throw (DimensionMismatch (" Factorization and RHS should have the same number of rows. " * 
1952+                 " Factorization has $(size (L, 2 ))  rows, but RHS has $(size (b, 1 ))  rows." 
1953+         end 
1954+         if  size (L, 2 ) !=  size (x, 1 )
1955+             throw (DimensionMismatch (" Factorization and solution should match sizes. " * 
1956+                 " Factorization has $(size (L, 1 ))  columns, but solution has $(size (x, 1 ))  rows." 
1957+         end 
1958+         if  size (x, 2 ) !=  size (b, 2 )
1959+             throw (DimensionMismatch (" Solution and RHS should have the same number of columns. " * 
1960+                 " Solution has $(size (x, 2 ))  columns, but RHS has $(size (b, 2 ))  columns." 
1961+         end 
1962+         if  ! issuccess (L)
1963+             s =  unsafe_load (pointer (L))
1964+             if  s. is_ll ==  1 
1965+                 throw (LinearAlgebra. PosDefException (s. minor))
1966+             else 
1967+                 throw (LinearAlgebra. ZeroPivotException (s. minor))
1968+             end 
1969+         end 
1970+ 
1971+         #  Just calling Dense(x) or Dense(b) will allocate new
1972+         #  `cholmod_dense_struct`s in CHOLMOD. Instead, we want to reuse
1973+         #  the existing memory. We can do this by creating new
1974+         #  `cholmod_dense_struct`s and filling them manually.
1975+         dense_x, dense_x_ptr =  wrap_dense_and_ptr (x)
1976+         dense_b, dense_b_ptr =  wrap_dense_and_ptr (b)
1977+ 
1978+         X_Handle =  Ptr {cholmod_dense_struct} (dense_x_ptr)
1979+         Y_Handle =  Ptr {cholmod_dense_struct} (C_NULL )
1980+         E_Handle =  Ptr {cholmod_dense_struct} (C_NULL )
1981+         status =  GC. @preserve  x dense_x b dense_b begin 
1982+             $ (cholname (:solve2 , TI))(
1983+                 CHOLMOD_A, L,
1984+                 Ref (dense_b), C_NULL ,
1985+                 Ref (X_Handle), C_NULL ,
1986+                 Ref (Y_Handle),
1987+                 Ref (E_Handle),
1988+                 getcommon ($ TI))
1989+         end 
1990+         if  Y_Handle !=  C_NULL 
1991+             free! (Y_Handle)
1992+         end 
1993+         if  E_Handle !=  C_NULL 
1994+             free! (E_Handle)
1995+         end 
1996+         @assert  ! iszero (status)
1997+ 
1998+         return  x
1999+     end 
2000+ end 
2001+ 
19082002# # Other convenience methods
19092003function  diag (F:: Factor{Tv, Ti} ) where  {Tv, Ti}
19102004    f =  unsafe_load (typedpointer (F))
0 commit comments