Skip to content

Commit 2b32b60

Browse files
authored
Unify initialization of SuiteSparse libraries (#677)
Create a unified initialization function for all SuiteSparse libraries (both in the stdlib and elsewhere like KLU). This remains lazily initialized. Should address #671. I wonder whether this should be truly `public` but KLU, AMD, and ParU, SPEX will rely on this to function correctly so probably should be. Also bumps wrapper generation. @gbaraldi not sure how to test this with your PR but tests on MinFEM.jl seem to pass locally for me.
1 parent 95b6ac4 commit 2b32b60

6 files changed

Lines changed: 145 additions & 116 deletions

File tree

gen/Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
44
SuiteSparse_jll = "bea87d4a-7f5b-5778-9afe-8cc45184846c"
55

66
[compat]
7-
Clang = "0.18"
8-
JuliaFormatter = "1.0.45"
7+
Clang = "0.18, 0.19"
8+
JuliaFormatter = "2"

gen/generator.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ else
2222
end
2323
include_dir = joinpath(artifact_dir, "include", "suitesparse") |> normpath
2424

25+
config_h = joinpath(include_dir, "SuiteSparse_config.h")
26+
@assert isfile(config_h)
27+
2528
cholmod_h = joinpath(include_dir, "cholmod.h")
2629
@assert isfile(cholmod_h)
2730

@@ -42,7 +45,7 @@ options["general"]["output_file_path"] = joinpath(@__DIR__, "..", "src/solvers/w
4245
args = get_default_args()
4346
push!(args, "-I$include_dir")
4447

45-
header_files = [cholmod_h, SuiteSparseQR_C_h, umfpack_h]
48+
header_files = [config_h, cholmod_h, SuiteSparseQR_C_h, umfpack_h]
4649

4750
ctx = create_context(header_files, args, options)
4851

src/solvers/LibSuiteSparse.jl

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,106 @@
11
module LibSuiteSparse
22

33
using SuiteSparse_jll
4+
import Libdl
45

56
const TRUE = Int32(1)
67
const FALSE = Int32(0)
78

89
include("wrappers.jl")
910

11+
const SUITESPARSE_MIN_VERSION = v"6.0.0"
12+
const BUILD_VERSION = VersionNumber(
13+
SUITESPARSE_MAIN_VERSION,
14+
SUITESPARSE_SUB_VERSION,
15+
SUITESPARSE_SUBSUB_VERSION
16+
)
17+
18+
public init_suitesparse
19+
20+
"""
21+
LibSuiteSparse.init_suitesparse
22+
23+
Internal function which is used to initialize the SuiteSparse libraries to the correct
24+
memory management functions. Any package which directly wraps one of the following
25+
SuiteSparse libraries *must* ensure that this function is called before the use of that
26+
library: AMD, CAMD, COLAMD, CCOLAMD, UMFPACK, CXSparse, CHOLMOD, KLU, BTF, LDL, RBio,
27+
SPQR, SPEX, and ParU
28+
29+
# Notes:
30+
- Currently this function only sets the memory management functions of SuiteSparse_config,
31+
however there are also override functions for `printf`, `hypot`, and `divcomplex`.
32+
- SuiteSparse_config, and this initialization function, is not a dependency of CSparse,
33+
GraphBLAS, or LAGraph.
34+
"""
35+
const init_suitesparse = Base.OncePerProcess{Nothing}() do
36+
try
37+
### Check if the linked library is compatible with the Julia code
38+
if Libdl.dlsym_e(Libdl.dlopen("libsuitesparseconfig"), :SuiteSparse_version) != C_NULL
39+
current_version_array = Vector{Cint}(undef, 3)
40+
SuiteSparse_version(current_version_array)
41+
current_version = VersionNumber(current_version_array...)
42+
else # SuiteSparse < 4.2.0 does not include SuiteSparse_version()
43+
current_version = v"0.0.0"
44+
end
45+
46+
47+
if current_version < SUITESPARSE_MIN_VERSION
48+
@warn """
49+
SuiteSparse version incompatibility
50+
51+
Julia was compiled with SuiteSparse version $BUILD_VERSION. It is
52+
currently linked with a version older than
53+
$(SUITESPARSE_MIN_VERSION). This might cause Julia to
54+
terminate when working with sparse matrix factorizations,
55+
e.g. solving systems of equations with \\.
56+
57+
It is recommended that you use Julia with a recent version
58+
of SuiteSparse, or download the generic binaries
59+
from www.julialang.org, which ship with the correct
60+
versions of all dependencies.
61+
"""
62+
elseif BUILD_VERSION.major != current_version.major
63+
@warn """
64+
SuiteSparse version incompatibility
65+
66+
Julia was compiled with SuiteSparse version $BUILD_VERSION. It is
67+
currently linked with version $current_version.
68+
This might cause Julia to terminate when working with
69+
sparse matrix factorizations, e.g. solving systems of
70+
equations with \\.
71+
72+
It is recommended that you use Julia with the same major
73+
version of SuiteSparse as the one used during the build, or
74+
download the generic binaries from www.julialang.org,
75+
which ship with the correct versions of all dependencies.
76+
"""
77+
end
78+
79+
current_version >= v"6.0.0" && SuiteSparse_start()
80+
81+
# Register gc tracked allocator if SuiteSparse is new enough
82+
if current_version >= v"7.0.0"
83+
SuiteSparse_config_malloc_func_set(cglobal(:jl_malloc, Ptr{Cvoid}))
84+
SuiteSparse_config_calloc_func_set(cglobal(:jl_calloc, Ptr{Cvoid}))
85+
SuiteSparse_config_realloc_func_set(cglobal(:jl_realloc, Ptr{Cvoid}))
86+
SuiteSparse_config_free_func_set(cglobal(:jl_free, Ptr{Cvoid}))
87+
elseif current_version >= v"4.2.0"
88+
cnfg = cglobal((:SuiteSparse_config, libsuitesparseconfig), Ptr{Cvoid})
89+
unsafe_store!(cnfg, cglobal(:jl_malloc, Ptr{Cvoid}), 1)
90+
unsafe_store!(cnfg, cglobal(:jl_calloc, Ptr{Cvoid}), 2)
91+
unsafe_store!(cnfg, cglobal(:jl_realloc, Ptr{Cvoid}), 3)
92+
unsafe_store!(cnfg, cglobal(:jl_free, Ptr{Cvoid}), 4)
93+
end
94+
95+
current_version >= v"6.0.0" && atexit() do
96+
SuiteSparse_finish()
97+
end
98+
99+
catch ex
100+
@error "Error during initialization of module LibSuiteSparse" exception=ex,catch_backtrace()
101+
end
102+
end
103+
10104
# exports
11105
const PREFIXES = ["cholmod_", "CHOLMOD_", "umfpack_"]
12106
for name in names(@__MODULE__; all=true), prefix in PREFIXES

src/solvers/cholmod.jl

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ import LinearAlgebra: (\), AdjointFactorization,
2424

2525
using SparseArrays
2626
using SparseArrays: getcolptr, AbstractSparseVecOrMat
27-
import Libdl
28-
2927
export
3028
Dense,
3129
Factor,
@@ -174,86 +172,19 @@ function newcommon(; print = 0) # no printing from CHOLMOD by default
174172
end
175173

176174
function getcommon(::Type{Int32})
177-
init_once()
175+
LibSuiteSparse.init_suitesparse()
178176
return get!(newcommon, task_local_storage(), :cholmod_common)::Ref{cholmod_common}
179177
end
180178

181179
function getcommon(::Type{Int64})
182-
init_once()
180+
LibSuiteSparse.init_suitesparse()
183181
return get!(newcommon_l, task_local_storage(), :cholmod_common_l)::Ref{cholmod_common}
184182
end
185183

186184
getcommon() = getcommon(Int)
187185

188186
const BUILD_VERSION = VersionNumber(CHOLMOD_MAIN_VERSION, CHOLMOD_SUB_VERSION, CHOLMOD_SUBSUB_VERSION)
189187

190-
const init_once = Base.OncePerProcess{Nothing}() do
191-
try
192-
### Check if the linked library is compatible with the Julia code
193-
if Libdl.dlsym_e(Libdl.dlopen("libcholmod"), :cholmod_version) != C_NULL
194-
current_version_array = Vector{Cint}(undef, 3)
195-
cholmod_version(current_version_array)
196-
current_version = VersionNumber(current_version_array...)
197-
else # CHOLMOD < 2.1.1 does not include cholmod_version()
198-
current_version = v"0.0.0"
199-
end
200-
201-
202-
if current_version < CHOLMOD_MIN_VERSION
203-
@warn """
204-
CHOLMOD version incompatibility
205-
206-
Julia was compiled with CHOLMOD version $BUILD_VERSION. It is
207-
currently linked with a version older than
208-
$(CHOLMOD_MIN_VERSION). This might cause Julia to
209-
terminate when working with sparse matrix factorizations,
210-
e.g. solving systems of equations with \\.
211-
212-
It is recommended that you use Julia with a recent version
213-
of CHOLMOD, or download the generic binaries
214-
from www.julialang.org, which ship with the correct
215-
versions of all dependencies.
216-
"""
217-
elseif BUILD_VERSION.major != current_version.major
218-
@warn """
219-
CHOLMOD version incompatibility
220-
221-
Julia was compiled with CHOLMOD version $BUILD_VERSION. It is
222-
currently linked with version $current_version.
223-
This might cause Julia to terminate when working with
224-
sparse matrix factorizations, e.g. solving systems of
225-
equations with \\.
226-
227-
It is recommended that you use Julia with the same major
228-
version of CHOLMOD as the one used during the build, or
229-
download the generic binaries from www.julialang.org,
230-
which ship with the correct versions of all dependencies.
231-
"""
232-
end
233-
234-
# Register gc tracked allocator if CHOLMOD is new enough
235-
if current_version >= v"4.0.3"
236-
ccall((:SuiteSparse_config_malloc_func_set, libsuitesparseconfig),
237-
Cvoid, (Ptr{Cvoid},), cglobal(:jl_malloc, Ptr{Cvoid}))
238-
ccall((:SuiteSparse_config_calloc_func_set, libsuitesparseconfig),
239-
Cvoid, (Ptr{Cvoid},), cglobal(:jl_calloc, Ptr{Cvoid}))
240-
ccall((:SuiteSparse_config_realloc_func_set, libsuitesparseconfig),
241-
Cvoid, (Ptr{Cvoid},), cglobal(:jl_realloc, Ptr{Cvoid}))
242-
ccall((:SuiteSparse_config_free_func_set, libsuitesparseconfig),
243-
Cvoid, (Ptr{Cvoid},), cglobal(:jl_free, Ptr{Cvoid}))
244-
elseif current_version >= v"3.0.0"
245-
cnfg = cglobal((:SuiteSparse_config, libsuitesparseconfig), Ptr{Cvoid})
246-
unsafe_store!(cnfg, cglobal(:jl_malloc, Ptr{Cvoid}), 1)
247-
unsafe_store!(cnfg, cglobal(:jl_calloc, Ptr{Cvoid}), 2)
248-
unsafe_store!(cnfg, cglobal(:jl_realloc, Ptr{Cvoid}), 3)
249-
unsafe_store!(cnfg, cglobal(:jl_free, Ptr{Cvoid}), 4)
250-
end
251-
252-
catch ex
253-
@error "Error during initialization of module CHOLMOD" exception=ex,catch_backtrace()
254-
end
255-
end
256-
257188
####################
258189
# Type definitions #
259190
####################

src/solvers/umfpack.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,15 +325,15 @@ is provided or `q` is `nothing`, UMFPACK's default is used. If the permutation i
325325
zero-based copy is made.
326326
327327
The `control` vector defaults to the Julia SparseArrays package's default configuration for UMFPACK (NB: this is modified from the UMFPACK defaults to
328-
disable iterative refinement), but can be changed by passing a vector of length `UMFPACK_CONTROL`, see the UMFPACK manual for possible configurations.
328+
disable iterative refinement), but can be changed by passing a vector of length `UMFPACK_CONTROL`, see the UMFPACK manual for possible configurations.
329329
For example to reenable iterative refinement:
330330
331331
umfpack_control = SparseArrays.UMFPACK.get_umfpack_control(Float64, Int64) # read Julia default configuration for a Float64 sparse matrix
332332
SparseArrays.UMFPACK.show_umf_ctrl(umfpack_control) # optional - display values
333333
umfpack_control[SparseArrays.UMFPACK.JL_UMFPACK_IRSTEP] = 2.0 # reenable iterative refinement (2 is UMFPACK default max iterative refinement steps)
334334
335335
Alu = lu(A; control = umfpack_control)
336-
x = Alu \\ b # solve Ax = b, including UMFPACK iterative refinement
336+
x = Alu \\ b # solve Ax = b, including UMFPACK iterative refinement
337337
338338
The individual components of the factorization `F` can be accessed by indexing:
339339
@@ -1036,6 +1036,7 @@ for Tv in (:Float64, :ComplexF64), Ti in UmfpackIndexTypes
10361036
# the control and info arrays
10371037
_defaults = Symbol(umf_nm("defaults", Tv, Ti))
10381038
@eval function get_umfpack_control(::Type{$Tv}, ::Type{$Ti})
1039+
LibSuiteSparse.init_suitesparse()
10391040
control = Vector{Float64}(undef, UMFPACK_CONTROL)
10401041
$_defaults(control)
10411042
# Put julia's config here

src/solvers/wrappers.jl

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@ function SuiteSparse_config_printf_func_get()
66
@ccall libsuitesparseconfig.SuiteSparse_config_printf_func_get()::Ptr{Cvoid}
77
end
88

9-
function cholmod_version(version)
10-
@ccall libcholmod.cholmod_version(version::Ptr{Cint})::Cint
11-
end
12-
13-
function cholmod_l_version(version)
14-
@ccall libcholmod.cholmod_l_version(version::Ptr{Cint})::Cint
15-
end
16-
179
function SuiteSparse_config_malloc_func_get()
1810
@ccall libsuitesparseconfig.SuiteSparse_config_malloc_func_get()::Ptr{Cvoid}
1911
end
@@ -155,6 +147,14 @@ function SuiteSparse_BLAS_integer_size()
155147
@ccall libsuitesparseconfig.SuiteSparse_BLAS_integer_size()::Csize_t
156148
end
157149

150+
function cholmod_version(version)
151+
@ccall libcholmod.cholmod_version(version::Ptr{Cint})::Cint
152+
end
153+
154+
function cholmod_l_version(version)
155+
@ccall libcholmod.cholmod_l_version(version::Ptr{Cint})::Cint
156+
end
157+
158158
@enum cholmod_query_t::UInt32 begin
159159
CHOLMOD_QUERY_HAS_GPL = 0
160160
CHOLMOD_QUERY_HAS_CHECK = 1
@@ -3348,38 +3348,6 @@ function umfpack_toc(stats)
33483348
@ccall libumfpack.umfpack_toc(stats::Ptr{Cdouble})::Cvoid
33493349
end
33503350

3351-
const CHOLMOD_PATTERN = 0
3352-
3353-
const CHOLMOD_REAL = 1
3354-
3355-
const CHOLMOD_COMPLEX = 2
3356-
3357-
const CHOLMOD_ZOMPLEX = 3
3358-
3359-
const CHOLMOD_DOUBLE = 0
3360-
3361-
const CHOLMOD_SINGLE = 4
3362-
3363-
const CHOLMOD_INT = 0
3364-
3365-
const CHOLMOD_LONG = 2
3366-
3367-
const CHOLMOD_DATE = "Feb 20, 2025"
3368-
3369-
const CHOLMOD_MAIN_VERSION = 5
3370-
3371-
const CHOLMOD_SUB_VERSION = 3
3372-
3373-
const CHOLMOD_SUBSUB_VERSION = 1
3374-
3375-
SUITESPARSE_VER_CODE(main, sub) = main * 1000 + sub
3376-
3377-
CHOLMOD_VER_CODE(main, sub) = SUITESPARSE_VER_CODE(main, sub)
3378-
3379-
const CHOLMOD_VERSION = CHOLMOD_VER_CODE(5, 3)
3380-
3381-
const _FILE_OFFSET_BITS = 64
3382-
33833351
const SUITESPARSE_OPENMP_MAX_THREADS = 1
33843352

33853353
const SUITESPARSE_OPENMP_GET_NUM_THREADS = 1
@@ -3412,6 +3380,8 @@ const SUITESPARSE_SUB_VERSION = 10
34123380

34133381
const SUITESPARSE_SUBSUB_VERSION = 1
34143382

3383+
SUITESPARSE_VER_CODE(main, sub) = main * 1000 + sub
3384+
34153385
const SUITESPARSE_VERSION = SUITESPARSE_VER_CODE(7, 10)
34163386

34173387
function SUITESPARSE__VERCODE(main, sub, patch)
@@ -3420,6 +3390,36 @@ end
34203390

34213391
const SUITESPARSE__VERSION = SUITESPARSE__VERCODE(7, 10, 1)
34223392

3393+
const CHOLMOD_PATTERN = 0
3394+
3395+
const CHOLMOD_REAL = 1
3396+
3397+
const CHOLMOD_COMPLEX = 2
3398+
3399+
const CHOLMOD_ZOMPLEX = 3
3400+
3401+
const CHOLMOD_DOUBLE = 0
3402+
3403+
const CHOLMOD_SINGLE = 4
3404+
3405+
const CHOLMOD_INT = 0
3406+
3407+
const CHOLMOD_LONG = 2
3408+
3409+
const CHOLMOD_DATE = "Feb 20, 2025"
3410+
3411+
const CHOLMOD_MAIN_VERSION = 5
3412+
3413+
const CHOLMOD_SUB_VERSION = 3
3414+
3415+
const CHOLMOD_SUBSUB_VERSION = 1
3416+
3417+
CHOLMOD_VER_CODE(main, sub) = SUITESPARSE_VER_CODE(main, sub)
3418+
3419+
const CHOLMOD_VERSION = CHOLMOD_VER_CODE(5, 3)
3420+
3421+
const _FILE_OFFSET_BITS = 64
3422+
34233423
const CHOLMOD__VERSION = SUITESPARSE__VERCODE(5, 3, 1)
34243424

34253425
const CHOLMOD_DEVICE_SUPERNODE_BUFFERS = 6

0 commit comments

Comments
 (0)