Skip to content

Commit 8b56957

Browse files
authored
Backports for release 1.13 (#701)
- [x] #672 - [x] #677 - [x] #700 - [x] #697 - [x] #692 - [x] #689 - [x] #694 - [x] #682
2 parents 5307f25 + a58422f commit 8b56957

13 files changed

Lines changed: 250 additions & 135 deletions

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/SparseArrays.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ using LinearAlgebra: AdjOrTrans, AdjointFactorization, TransposeFactorization, m
1717
import Base: +, -, *, \, /, ==, zero
1818
import Base: Matrix, Vector
1919
import LinearAlgebra: mul!, ldiv!, rdiv!, cholesky, adjoint!, diag, eigen, dot,
20-
issymmetric, istril, istriu, lu, tr, transpose!, tril!, triu!, isbanded,
20+
issymmetric, istril, istriu, lu, tr, transpose!, tril!, triu!, isbanded, isdiag,
2121
cond, diagm, factorize, ishermitian, norm, opnorm, lmul!, rmul!, tril, triu,
22-
matprod_dest, generic_matvecmul!, generic_matmatmul!, copytrito!
22+
matprod_dest, generic_matvecmul!, generic_matmatmul!, generic_matmatmul_wrapper!, copytrito!
2323

2424
import Base: adjoint, argmin, argmax, Array, broadcast, circshift!, complex, Complex,
2525
conj, conj!, convert, copy, copy!, copyto!, count, diff, findall, findmax, findmin,

src/linalg.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ Base.@constprop :aggressive function spdensemul!(C, tA, tB, A, B, alpha, beta)
6262
_At_or_Ac_mul_B!(transpose, C, A, wrap(B, tB), alpha, beta)
6363
elseif tA_uc == 'C'
6464
_At_or_Ac_mul_B!(adjoint, C, A, wrap(B, tB), alpha, beta)
65-
elseif tA_uc in ('S', 'H') && tB_uc == 'N'
65+
elseif tA_uc in ('S', 'H')
6666
rangefun = isuppercase(tA) ? nzrangeup : nzrangelo
6767
diagop = tA_uc == 'S' ? identity : real
6868
odiagop = tA_uc == 'S' ? transpose : adjoint
6969
T = eltype(C)
70-
_mul!(rangefun, diagop, odiagop, C, A, B, T(alpha), T(beta))
70+
_mul!(rangefun, diagop, odiagop, C, A, wrap(B, tB), T(alpha), T(beta))
7171
else
7272
@stable_muladdmul LinearAlgebra._generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), MulAddMul(alpha, beta))
7373
end
@@ -117,7 +117,7 @@ function _At_or_Ac_mul_B!(tfun::Function, C, A, B, α, β)
117117
C
118118
end
119119

120-
Base.@constprop :aggressive function generic_matmatmul!(C::StridedMatrix, tA, tB, A::DenseMatrixUnion, B::SparseMatrixCSCUnion2, alpha::Number, beta::Number)
120+
Base.@constprop :aggressive function generic_matmatmul_wrapper!(C::StridedMatrix, tA, tB, A::DenseMatrixUnion, B::SparseMatrixCSCUnion2, alpha::Number, beta::Number, ::LinearAlgebra.BlasFlag.SyrkHerkGemm)
121121
transA = tA == 'N' ? identity : tA == 'T' ? transpose : adjoint
122122
if tB == 'N'
123123
_spmul!(C, transA(A), B, alpha, beta)
@@ -128,6 +128,9 @@ Base.@constprop :aggressive function generic_matmatmul!(C::StridedMatrix, tA, tB
128128
end
129129
return C
130130
end
131+
Base.@constprop :aggressive generic_matmatmul_wrapper!(C::StridedMatrix, tA, tB, A::DenseMatrixUnion, B::SparseMatrixCSCUnion2, alpha::Number, beta::Number, @nospecialize(val)) =
132+
LinearAlgebra._generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), alpha, beta)
133+
131134
function _spmul!(C::StridedMatrix, X::DenseMatrixUnion, A::SparseMatrixCSCUnion2, α::Number, β::Number)
132135
mX, nX = size(X)
133136
nX == size(A, 1) ||
@@ -1742,7 +1745,7 @@ function opnormestinv(A::AbstractSparseMatrixCSC{T}, t::Integer = min(2,maximum(
17421745
repeated = true
17431746
end
17441747
end
1745-
if !repeated
1748+
if !repeated && 2^(n-1) 2t #we need enough non-parallel ±1 vectors
17461749
saux2 = S[1:n,j]' * S_old[1:n,1:t]
17471750
if _any_abs_eq(saux2,n)
17481751
repeated = true

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 & 73 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
####################
@@ -1356,11 +1287,9 @@ function size(F::Factor, i::Integer)
13561287
return 1
13571288
end
13581289
size(F::Factor) = (size(F, 1), size(F, 2))
1359-
axes(A::Union{Dense,Sparse,Factor}) = map(Base.OneTo, size(A))
13601290

13611291
IndexStyle(::Type{<:Dense}) = IndexLinear()
13621292

1363-
size(FC::FactorComponent, i::Integer) = size(FC.F, i)
13641293
size(FC::FactorComponent) = size(FC.F)
13651294

13661295
adjoint(FC::FactorComponent{Tv,:L}) where {Tv} = FactorComponent{Tv,:U}(FC.F)

src/solvers/spqr.jl

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ struct QRSparseQ{Tv<:CHOLMOD.VTypes,Ti<:Integer} <: AbstractQ{Tv}
115115
end
116116

117117
Base.size(Q::QRSparseQ) = (size(Q.factors, 1), size(Q.factors, 1))
118-
Base.axes(Q::QRSparseQ) = map(Base.OneTo, size(Q))
119118

120119
Matrix{T}(Q::QRSparseQ) where {T} = lmul!(Q, Matrix{T}(I, size(Q, 1), min(size(Q, 1), Q.n)))
121120

@@ -146,7 +145,6 @@ function Base.size(F::QRSparse, i::Integer)
146145
throw(ArgumentError("second argument must be positive"))
147146
end
148147
end
149-
Base.axes(F::QRSparse) = map(Base.OneTo, size(F))
150148

151149
# From SPQR manual p. 6
152150
_default_tol(A::AbstractSparseMatrixCSC) =
@@ -510,8 +508,23 @@ function LinearAlgebra.ldiv!(X::StridedVecOrMat{T}, F::QRSparse{T}, B::StridedVe
510508
lmul!(adjoint(F.Q), W0)
511509

512510
# Solve R*X = Q'*P*B
513-
ldiv!(UpperTriangular(@view(F.R[Base.OneTo(rnk), Base.OneTo(rnk)])),
514-
@view(W0[Base.OneTo(rnk), :]))
511+
#
512+
# We call generic_trimatdiv! directly instead of going through
513+
# ldiv!(UpperTriangular(R_sub), ...) for two reasons:
514+
# 1. UpperTriangular requires a square matrix, but F.R is m×n
515+
# so we can only take a column view R[:, 1:rnk] (which is
516+
# m×rnk, not square). A row+column view R[1:rnk, 1:rnk]
517+
# would be square but doesn't match SparseMatrixCSCView,
518+
# causing dispatch to a slow generic fallback.
519+
# 2. generic_trimatdiv! is what UpperTriangular ldiv! dispatches
520+
# to anyway — calling it directly with uploc='U', isunitc='N',
521+
# tfun=identity is equivalent. The back-substitution loop
522+
# iterates over axes(B,1) = 1:rnk and searchsortedlast
523+
# excludes entries with row > j, so the extra rows in the
524+
# column view are never accessed.
525+
W_rnk = @view(W0[Base.OneTo(rnk), :])
526+
LinearAlgebra.generic_trimatdiv!(W_rnk, 'U', 'N', identity,
527+
@view(F.R[:, Base.OneTo(rnk)]), W_rnk)
515528

516529
# Apply right permutation: scatter solved rows into X using cpiv directly.
517530
# Zero X first so free variables (beyond rank) are zero in the basic solution.

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

0 commit comments

Comments
 (0)