diff --git a/gen/Project.toml b/gen/Project.toml index 19c05f83..c2f0c14f 100644 --- a/gen/Project.toml +++ b/gen/Project.toml @@ -4,5 +4,5 @@ JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" SuiteSparse_jll = "bea87d4a-7f5b-5778-9afe-8cc45184846c" [compat] -Clang = "0.18" -JuliaFormatter = "1.0.45" +Clang = "0.18, 0.19" +JuliaFormatter = "2" diff --git a/gen/generator.jl b/gen/generator.jl index 7fbdb2c2..80123c1c 100755 --- a/gen/generator.jl +++ b/gen/generator.jl @@ -22,6 +22,9 @@ else end include_dir = joinpath(artifact_dir, "include", "suitesparse") |> normpath +config_h = joinpath(include_dir, "SuiteSparse_config.h") +@assert isfile(config_h) + cholmod_h = joinpath(include_dir, "cholmod.h") @assert isfile(cholmod_h) @@ -42,7 +45,7 @@ options["general"]["output_file_path"] = joinpath(@__DIR__, "..", "src/solvers/w args = get_default_args() push!(args, "-I$include_dir") -header_files = [cholmod_h, SuiteSparseQR_C_h, umfpack_h] +header_files = [config_h, cholmod_h, SuiteSparseQR_C_h, umfpack_h] ctx = create_context(header_files, args, options) diff --git a/src/solvers/LibSuiteSparse.jl b/src/solvers/LibSuiteSparse.jl index 2db63858..13747243 100644 --- a/src/solvers/LibSuiteSparse.jl +++ b/src/solvers/LibSuiteSparse.jl @@ -1,12 +1,106 @@ module LibSuiteSparse using SuiteSparse_jll +import Libdl const TRUE = Int32(1) const FALSE = Int32(0) include("wrappers.jl") +const SUITESPARSE_MIN_VERSION = v"6.0.0" +const BUILD_VERSION = VersionNumber( + SUITESPARSE_MAIN_VERSION, + SUITESPARSE_SUB_VERSION, + SUITESPARSE_SUBSUB_VERSION +) + +public init_suitesparse + +""" + LibSuiteSparse.init_suitesparse + +Internal function which is used to initialize the SuiteSparse libraries to the correct + memory management functions. Any package which directly wraps one of the following + SuiteSparse libraries *must* ensure that this function is called before the use of that + library: AMD, CAMD, COLAMD, CCOLAMD, UMFPACK, CXSparse, CHOLMOD, KLU, BTF, LDL, RBio, + SPQR, SPEX, and ParU + +# Notes: +- Currently this function only sets the memory management functions of SuiteSparse_config, + however there are also override functions for `printf`, `hypot`, and `divcomplex`. +- SuiteSparse_config, and this initialization function, is not a dependency of CSparse, + GraphBLAS, or LAGraph. +""" +const init_suitesparse = Base.OncePerProcess{Nothing}() do + try + ### Check if the linked library is compatible with the Julia code + if Libdl.dlsym_e(Libdl.dlopen("libsuitesparseconfig"), :SuiteSparse_version) != C_NULL + current_version_array = Vector{Cint}(undef, 3) + SuiteSparse_version(current_version_array) + current_version = VersionNumber(current_version_array...) + else # SuiteSparse < 4.2.0 does not include SuiteSparse_version() + current_version = v"0.0.0" + end + + + if current_version < SUITESPARSE_MIN_VERSION + @warn """ + SuiteSparse version incompatibility + + Julia was compiled with SuiteSparse version $BUILD_VERSION. It is + currently linked with a version older than + $(SUITESPARSE_MIN_VERSION). This might cause Julia to + terminate when working with sparse matrix factorizations, + e.g. solving systems of equations with \\. + + It is recommended that you use Julia with a recent version + of SuiteSparse, or download the generic binaries + from www.julialang.org, which ship with the correct + versions of all dependencies. + """ + elseif BUILD_VERSION.major != current_version.major + @warn """ + SuiteSparse version incompatibility + + Julia was compiled with SuiteSparse version $BUILD_VERSION. It is + currently linked with version $current_version. + This might cause Julia to terminate when working with + sparse matrix factorizations, e.g. solving systems of + equations with \\. + + It is recommended that you use Julia with the same major + version of SuiteSparse as the one used during the build, or + download the generic binaries from www.julialang.org, + which ship with the correct versions of all dependencies. + """ + end + + current_version >= v"6.0.0" && SuiteSparse_start() + + # Register gc tracked allocator if SuiteSparse is new enough + if current_version >= v"7.0.0" + SuiteSparse_config_malloc_func_set(cglobal(:jl_malloc, Ptr{Cvoid})) + SuiteSparse_config_calloc_func_set(cglobal(:jl_calloc, Ptr{Cvoid})) + SuiteSparse_config_realloc_func_set(cglobal(:jl_realloc, Ptr{Cvoid})) + SuiteSparse_config_free_func_set(cglobal(:jl_free, Ptr{Cvoid})) + elseif current_version >= v"4.2.0" + cnfg = cglobal((:SuiteSparse_config, libsuitesparseconfig), Ptr{Cvoid}) + unsafe_store!(cnfg, cglobal(:jl_malloc, Ptr{Cvoid}), 1) + unsafe_store!(cnfg, cglobal(:jl_calloc, Ptr{Cvoid}), 2) + unsafe_store!(cnfg, cglobal(:jl_realloc, Ptr{Cvoid}), 3) + unsafe_store!(cnfg, cglobal(:jl_free, Ptr{Cvoid}), 4) + end + + current_version >= v"6.0.0" && atexit() do + SuiteSparse_finish() + end + + catch ex + @error "Error during initialization of module LibSuiteSparse" exception=ex,catch_backtrace() + end +end + # exports const PREFIXES = ["cholmod_", "CHOLMOD_", "umfpack_"] for name in names(@__MODULE__; all=true), prefix in PREFIXES diff --git a/src/solvers/cholmod.jl b/src/solvers/cholmod.jl index d73dce69..3fd2718f 100644 --- a/src/solvers/cholmod.jl +++ b/src/solvers/cholmod.jl @@ -24,8 +24,6 @@ import LinearAlgebra: (\), AdjointFactorization, using SparseArrays using SparseArrays: getcolptr, AbstractSparseVecOrMat -import Libdl - export Dense, Factor, @@ -174,12 +172,12 @@ function newcommon(; print = 0) # no printing from CHOLMOD by default end function getcommon(::Type{Int32}) - init_once() + LibSuiteSparse.init_suitesparse() return get!(newcommon, task_local_storage(), :cholmod_common)::Ref{cholmod_common} end function getcommon(::Type{Int64}) - init_once() + LibSuiteSparse.init_suitesparse() return get!(newcommon_l, task_local_storage(), :cholmod_common_l)::Ref{cholmod_common} end @@ -187,73 +185,6 @@ getcommon() = getcommon(Int) const BUILD_VERSION = VersionNumber(CHOLMOD_MAIN_VERSION, CHOLMOD_SUB_VERSION, CHOLMOD_SUBSUB_VERSION) -const init_once = Base.OncePerProcess{Nothing}() do - try - ### Check if the linked library is compatible with the Julia code - if Libdl.dlsym_e(Libdl.dlopen("libcholmod"), :cholmod_version) != C_NULL - current_version_array = Vector{Cint}(undef, 3) - cholmod_version(current_version_array) - current_version = VersionNumber(current_version_array...) - else # CHOLMOD < 2.1.1 does not include cholmod_version() - current_version = v"0.0.0" - end - - - if current_version < CHOLMOD_MIN_VERSION - @warn """ - CHOLMOD version incompatibility - - Julia was compiled with CHOLMOD version $BUILD_VERSION. It is - currently linked with a version older than - $(CHOLMOD_MIN_VERSION). This might cause Julia to - terminate when working with sparse matrix factorizations, - e.g. solving systems of equations with \\. - - It is recommended that you use Julia with a recent version - of CHOLMOD, or download the generic binaries - from www.julialang.org, which ship with the correct - versions of all dependencies. - """ - elseif BUILD_VERSION.major != current_version.major - @warn """ - CHOLMOD version incompatibility - - Julia was compiled with CHOLMOD version $BUILD_VERSION. It is - currently linked with version $current_version. - This might cause Julia to terminate when working with - sparse matrix factorizations, e.g. solving systems of - equations with \\. - - It is recommended that you use Julia with the same major - version of CHOLMOD as the one used during the build, or - download the generic binaries from www.julialang.org, - which ship with the correct versions of all dependencies. - """ - end - - # Register gc tracked allocator if CHOLMOD is new enough - if current_version >= v"4.0.3" - ccall((:SuiteSparse_config_malloc_func_set, libsuitesparseconfig), - Cvoid, (Ptr{Cvoid},), cglobal(:jl_malloc, Ptr{Cvoid})) - ccall((:SuiteSparse_config_calloc_func_set, libsuitesparseconfig), - Cvoid, (Ptr{Cvoid},), cglobal(:jl_calloc, Ptr{Cvoid})) - ccall((:SuiteSparse_config_realloc_func_set, libsuitesparseconfig), - Cvoid, (Ptr{Cvoid},), cglobal(:jl_realloc, Ptr{Cvoid})) - ccall((:SuiteSparse_config_free_func_set, libsuitesparseconfig), - Cvoid, (Ptr{Cvoid},), cglobal(:jl_free, Ptr{Cvoid})) - elseif current_version >= v"3.0.0" - cnfg = cglobal((:SuiteSparse_config, libsuitesparseconfig), Ptr{Cvoid}) - unsafe_store!(cnfg, cglobal(:jl_malloc, Ptr{Cvoid}), 1) - unsafe_store!(cnfg, cglobal(:jl_calloc, Ptr{Cvoid}), 2) - unsafe_store!(cnfg, cglobal(:jl_realloc, Ptr{Cvoid}), 3) - unsafe_store!(cnfg, cglobal(:jl_free, Ptr{Cvoid}), 4) - end - - catch ex - @error "Error during initialization of module CHOLMOD" exception=ex,catch_backtrace() - end -end - #################### # Type definitions # #################### diff --git a/src/solvers/umfpack.jl b/src/solvers/umfpack.jl index b267f0b6..7a8458d9 100644 --- a/src/solvers/umfpack.jl +++ b/src/solvers/umfpack.jl @@ -325,7 +325,7 @@ is provided or `q` is `nothing`, UMFPACK's default is used. If the permutation i zero-based copy is made. The `control` vector defaults to the Julia SparseArrays package's default configuration for UMFPACK (NB: this is modified from the UMFPACK defaults to -disable iterative refinement), but can be changed by passing a vector of length `UMFPACK_CONTROL`, see the UMFPACK manual for possible configurations. +disable iterative refinement), but can be changed by passing a vector of length `UMFPACK_CONTROL`, see the UMFPACK manual for possible configurations. For example to reenable iterative refinement: umfpack_control = SparseArrays.UMFPACK.get_umfpack_control(Float64, Int64) # read Julia default configuration for a Float64 sparse matrix @@ -333,7 +333,7 @@ For example to reenable iterative refinement: umfpack_control[SparseArrays.UMFPACK.JL_UMFPACK_IRSTEP] = 2.0 # reenable iterative refinement (2 is UMFPACK default max iterative refinement steps) Alu = lu(A; control = umfpack_control) - x = Alu \\ b # solve Ax = b, including UMFPACK iterative refinement + x = Alu \\ b # solve Ax = b, including UMFPACK iterative refinement The individual components of the factorization `F` can be accessed by indexing: @@ -1036,6 +1036,7 @@ for Tv in (:Float64, :ComplexF64), Ti in UmfpackIndexTypes # the control and info arrays _defaults = Symbol(umf_nm("defaults", Tv, Ti)) @eval function get_umfpack_control(::Type{$Tv}, ::Type{$Ti}) + LibSuiteSparse.init_suitesparse() control = Vector{Float64}(undef, UMFPACK_CONTROL) $_defaults(control) # Put julia's config here diff --git a/src/solvers/wrappers.jl b/src/solvers/wrappers.jl index c528976e..6e59b011 100644 --- a/src/solvers/wrappers.jl +++ b/src/solvers/wrappers.jl @@ -6,14 +6,6 @@ function SuiteSparse_config_printf_func_get() @ccall libsuitesparseconfig.SuiteSparse_config_printf_func_get()::Ptr{Cvoid} end -function cholmod_version(version) - @ccall libcholmod.cholmod_version(version::Ptr{Cint})::Cint -end - -function cholmod_l_version(version) - @ccall libcholmod.cholmod_l_version(version::Ptr{Cint})::Cint -end - function SuiteSparse_config_malloc_func_get() @ccall libsuitesparseconfig.SuiteSparse_config_malloc_func_get()::Ptr{Cvoid} end @@ -155,6 +147,14 @@ function SuiteSparse_BLAS_integer_size() @ccall libsuitesparseconfig.SuiteSparse_BLAS_integer_size()::Csize_t end +function cholmod_version(version) + @ccall libcholmod.cholmod_version(version::Ptr{Cint})::Cint +end + +function cholmod_l_version(version) + @ccall libcholmod.cholmod_l_version(version::Ptr{Cint})::Cint +end + @enum cholmod_query_t::UInt32 begin CHOLMOD_QUERY_HAS_GPL = 0 CHOLMOD_QUERY_HAS_CHECK = 1 @@ -3348,38 +3348,6 @@ function umfpack_toc(stats) @ccall libumfpack.umfpack_toc(stats::Ptr{Cdouble})::Cvoid end -const CHOLMOD_PATTERN = 0 - -const CHOLMOD_REAL = 1 - -const CHOLMOD_COMPLEX = 2 - -const CHOLMOD_ZOMPLEX = 3 - -const CHOLMOD_DOUBLE = 0 - -const CHOLMOD_SINGLE = 4 - -const CHOLMOD_INT = 0 - -const CHOLMOD_LONG = 2 - -const CHOLMOD_DATE = "Feb 20, 2025" - -const CHOLMOD_MAIN_VERSION = 5 - -const CHOLMOD_SUB_VERSION = 3 - -const CHOLMOD_SUBSUB_VERSION = 1 - -SUITESPARSE_VER_CODE(main, sub) = main * 1000 + sub - -CHOLMOD_VER_CODE(main, sub) = SUITESPARSE_VER_CODE(main, sub) - -const CHOLMOD_VERSION = CHOLMOD_VER_CODE(5, 3) - -const _FILE_OFFSET_BITS = 64 - const SUITESPARSE_OPENMP_MAX_THREADS = 1 const SUITESPARSE_OPENMP_GET_NUM_THREADS = 1 @@ -3412,6 +3380,8 @@ const SUITESPARSE_SUB_VERSION = 10 const SUITESPARSE_SUBSUB_VERSION = 1 +SUITESPARSE_VER_CODE(main, sub) = main * 1000 + sub + const SUITESPARSE_VERSION = SUITESPARSE_VER_CODE(7, 10) function SUITESPARSE__VERCODE(main, sub, patch) @@ -3420,6 +3390,36 @@ end const SUITESPARSE__VERSION = SUITESPARSE__VERCODE(7, 10, 1) +const CHOLMOD_PATTERN = 0 + +const CHOLMOD_REAL = 1 + +const CHOLMOD_COMPLEX = 2 + +const CHOLMOD_ZOMPLEX = 3 + +const CHOLMOD_DOUBLE = 0 + +const CHOLMOD_SINGLE = 4 + +const CHOLMOD_INT = 0 + +const CHOLMOD_LONG = 2 + +const CHOLMOD_DATE = "Feb 20, 2025" + +const CHOLMOD_MAIN_VERSION = 5 + +const CHOLMOD_SUB_VERSION = 3 + +const CHOLMOD_SUBSUB_VERSION = 1 + +CHOLMOD_VER_CODE(main, sub) = SUITESPARSE_VER_CODE(main, sub) + +const CHOLMOD_VERSION = CHOLMOD_VER_CODE(5, 3) + +const _FILE_OFFSET_BITS = 64 + const CHOLMOD__VERSION = SUITESPARSE__VERCODE(5, 3, 1) const CHOLMOD_DEVICE_SUPERNODE_BUFFERS = 6