Skip to content

Commit 5e8f00c

Browse files
authored
Backports to release-1.13: #709, #710, #712, #716, #720, #721, #722 (#723)
Backports seven PRs to `release-1.13` via `git cherry-pick -x`: - #709 — fix cholesky linear solve with sparse rhs (commit 754280c) - #710 — make `zero` preserve indextype (commit 405a8d5) - #712 — Fix and test `reuse_symbolic=false` for UMFPACK `lu!()` (commit 40d5de2) - #716 — test: avoid method-overwrite warnings in forbidproperties.jl (commit f7b1e19) - #720 — Add trailing whitespace check (commit 5926672) - #721 — sparsematrix: fix `swaprows!` storage moves (commit 85f7b74) - #722 — fix `findmin`/`findmax` with `NaN` (commit 3f9b6fb) All cherry-picked cleanly with no conflicts. Each commit retains its `(cherry picked from commit …)` trailer.
2 parents 0ecfa6f + 32f78fc commit 5e8f00c

17 files changed

Lines changed: 201 additions & 24 deletions

.ci/check-whitespace.jl

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env julia
2+
3+
const patterns = split("""
4+
*.jl
5+
*.md
6+
*.yml
7+
*Makefile
8+
""")
9+
10+
const is_gha = something(tryparse(Bool, get(ENV, "GITHUB_ACTIONS", "false")), false)
11+
12+
# Note: `git ls-files` gives `/` as a path separator on Windows,
13+
# so we just use `/` for all platforms.
14+
allow_tabs(path) =
15+
endswith(path, "Makefile") ||
16+
endswith(path, ".make") ||
17+
endswith(path, ".mk")
18+
19+
function check_whitespace()
20+
errors = Set{Tuple{String,Int,String}}()
21+
files_to_check = filter(arg -> !startswith(arg, "-"), ARGS)
22+
if isempty(files_to_check)
23+
if "--stdin" in ARGS
24+
files_to_check = collect(eachline(stdin))
25+
else
26+
files_to_check = collect(eachline(`git ls-files -- $patterns`))
27+
end
28+
end
29+
30+
files_fixed = 0
31+
if "--fix" in ARGS
32+
for path in files_to_check
33+
content = newcontent = read(path, String)
34+
isempty(content) && continue
35+
if !allow_tabs(path)
36+
tabpattern = r"^([ \t]+)"m => (x -> replace(x, r"((?: {4})*)( *\t)" => s"\1 ")) # Replace tab sequences at start of line after any number of 4-space groups
37+
newcontent = replace(newcontent, tabpattern)
38+
end
39+
newcontent = replace(newcontent,
40+
r"\s*$" => '\n', # Remove trailing whitespace and normalize line ending at eof
41+
r"\s*?[\r\n]" => '\n', # Remove trailing whitespace and normalize line endings on each line
42+
r"\xa0" => ' ' # Replace non-breaking spaces
43+
)
44+
if content != newcontent
45+
write(path, newcontent)
46+
files_fixed += 1
47+
end
48+
end
49+
if files_fixed > 0
50+
println(stderr, "Fixed whitespace issues in $files_fixed files.")
51+
end
52+
end
53+
54+
for path in files_to_check
55+
lineno = 0
56+
non_blank = 0
57+
58+
file_err(msg) = push!(errors, (path, 0, msg))
59+
line_err(msg) = push!(errors, (path, lineno, msg))
60+
61+
isfile(path) || continue
62+
for line in eachline(path, keep=true)
63+
lineno += 1
64+
contains(line, '\r') && file_err("non-UNIX line endings")
65+
contains(line, '\ua0') && line_err("non-breaking space")
66+
allow_tabs(path) ||
67+
contains(line, '\t') && line_err("tab")
68+
endswith(line, '\n') || line_err("no trailing newline")
69+
line = chomp(line)
70+
endswith(line, r"\s") && line_err("trailing whitespace")
71+
contains(line, r"\S") && (non_blank = lineno)
72+
end
73+
non_blank < lineno && line_err("trailing blank lines")
74+
end
75+
76+
if isempty(errors)
77+
println(stderr, "Whitespace check found no issues.")
78+
exit(0)
79+
else
80+
println(stderr, "Whitespace check found $(length(errors)) issues:")
81+
for (path, lineno, msg) in sort!(collect(errors))
82+
if lineno == 0
83+
println(stderr, "$path -- $msg")
84+
if is_gha
85+
println(stdout, "::warning title=Whitespace check,file=", path, "::", msg)
86+
end
87+
else
88+
println(stderr, "$path:$lineno -- $msg")
89+
if is_gha
90+
println(stdout, "::warning title=Whitespace check,file=", path, ",line=", lineno, "::", msg)
91+
end
92+
end
93+
end
94+
exit(1)
95+
end
96+
end
97+
98+
check_whitespace()

.github/workflows/Whitespace.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Whitespace
2+
3+
permissions: {}
4+
5+
on:
6+
push:
7+
branches:
8+
- main
9+
pull_request:
10+
11+
jobs:
12+
whitespace:
13+
name: Check whitespace
14+
runs-on: ubuntu-latest
15+
timeout-minutes: 2
16+
steps:
17+
- name: Checkout the JuliaSparse/SparseArrays.jl repository
18+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
19+
with:
20+
persist-credentials: false
21+
- uses: julia-actions/setup-julia@f6f565d9f7cf12f53dc8045742460d6260ad3b39 # v3.0.1
22+
with:
23+
version: '1.11.6'
24+
- name: Check whitespace
25+
run: |
26+
julia .ci/check-whitespace.jl

gen/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ all: clean download
77
julia --project generator.jl ./SuiteSparse-$(VER)
88

99
clean:
10-
rm -fr *.tar.gz SuiteSparse*
10+
rm -fr *.tar.gz SuiteSparse*
1111

1212
download:
1313
curl -L -O https://github.com/JuliaBinaryWrappers/SuiteSparse_jll.jl/releases/download/SuiteSparse-v$(VER)%2B0/SuiteSparse.v$(VER).x86_64-linux-gnu.tar.gz

src/SparseArrays.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ if Base.USE_GPL_LIBS
8585
include("solvers/spqr.jl")
8686
end
8787

88-
zero(a::AbstractSparseArray) = spzeros(eltype(a), size(a)...)
88+
zero(a::AbstractSparseArray{Tv,Ti}) where {Tv,Ti} = spzeros(Tv, Ti, size(a)...)
8989

9090
LinearAlgebra.diagzero(D::Diagonal{<:AbstractSparseMatrix{T}},i,j) where {T} =
9191
spzeros(T, size(D.diag[i], 1), size(D.diag[j], 2))

src/solvers/cholmod.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ function Sparse(A::SparseMatrixCSC{<:Union{ComplexF16, ComplexF32}}, stype::Inte
990990
end
991991

992992
# convert SparseVectors into CHOLMOD Sparse types through a mx1 CSC matrix
993-
Sparse(A::SparseVector) = Sparse(SparseMatrixCSC(A))
993+
Sparse(A::SparseVector) = Sparse(SparseMatrixCSC(A), 0)
994994
function Sparse{Tv, Ti}(A::SparseMatrixCSC) where {Tv<:VTypes, Ti<:ITypes}
995995
o = Sparse{Tv, Ti}(A, 0)
996996
# check if array is symmetric and change stype if it is
@@ -1135,6 +1135,7 @@ function SparseVector{Tv, Ti}(A::Sparse{Tv, Ti}) where {Tv, Ti<:ITypes}
11351135
end
11361136
args = _extract_args(s, Tv)
11371137
s.sorted == 0 && _sort_buffers!(args...);
1138+
_trim_nz_builder!(args...)
11381139
return SparseVector(args[1], args[4], args[5])
11391140
end
11401141

src/solvers/spqr.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function _qr!(ordering::Integer, tol::Real, econ::Integer, getCTX::Integer,
8585
# Free memory allocated by SPQR. This call will make sure that the
8686
# correct deallocator function is called and that the memory count in
8787
# the common struct is updated
88-
Ti === Int64 ?
88+
Ti === Int64 ?
8989
cholmod_l_free(n, sizeof(Ti), e, CHOLMOD.getcommon(Ti)) :
9090
cholmod_free(n, sizeof(Ti), e, CHOLMOD.getcommon(Ti))
9191
end
@@ -100,7 +100,7 @@ function _qr!(ordering::Integer, tol::Real, econ::Integer, getCTX::Integer,
100100
# Free memory allocated by SPQR. This call will make sure that the
101101
# correct deallocator function is called and that the memory count in
102102
# the common struct is updated
103-
Ti === Int64 ?
103+
Ti === Int64 ?
104104
cholmod_l_free(m, sizeof(Ti), hpinv, CHOLMOD.getcommon(Ti)) :
105105
cholmod_free(m, sizeof(Ti), hpinv, CHOLMOD.getcommon(Ti))
106106
end

src/solvers/umfpack.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,8 @@ function lu!(F::UmfpackLU{Tv, Ti}, S::AbstractSparseMatrixCSC;
471471
return lu!(F; reuse_symbolic, check, q)
472472
end
473473

474-
function lu!(F::UmfpackLU; check::Bool=true, reuse_symbolic::Bool=true, q=nothing)
474+
function lu!(F::UmfpackLU{Tv, Ti}; check::Bool=true, reuse_symbolic::Bool=true,
475+
q=nothing) where {Tv, Ti}
475476
if !reuse_symbolic && _isnotnull(F.symbolic)
476477
F.symbolic = Symbolic{Tv, Ti}(C_NULL)
477478
end

src/sparseconvert.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,3 @@ function _sparse_gen(m, n, newcolptr, newrowval, newnzval)
280280
newcolptr[1] = 1
281281
SparseMatrixCSC(m, n, newcolptr, newrowval, newnzval)
282282
end
283-

src/sparsematrix.jl

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4513,7 +4513,7 @@ function Base.swaprows!(A::AbstractSparseMatrixCSC, i, j)
45134513
iidx = searchsortedfirst(@view(rows[rr]), i)
45144514
has_i = iidx <= length(rr) && rows[rr[iidx]] == i
45154515

4516-
jrange = has_i ? (iidx:last(rr)) : rr
4516+
jrange = has_i ? (rr[iidx]:last(rr)) : rr
45174517
jidx = searchsortedlast(@view(rows[jrange]), j)
45184518
has_j = jidx != 0 && rows[jrange[jidx]] == j
45194519

@@ -4527,18 +4527,16 @@ function Base.swaprows!(A::AbstractSparseMatrixCSC, i, j)
45274527
# Update the rowval and then rotate both nonzeros
45284528
# and the remaining rowvals into the correct place
45294529
rows[rr[iidx]] = j
4530-
jidx == 0 && continue
45314530
rotate_range = rr[iidx]:jrange[jidx]
4532-
circshift!(@view(vals[rotate_range]), 1)
4533-
circshift!(@view(rows[rotate_range]), 1)
4531+
circshift!(@view(vals[rotate_range]), -1)
4532+
circshift!(@view(rows[rotate_range]), -1)
45344533
else
45354534
# Same as i, but in the opposite direction
45364535
@assert has_j
45374536
rows[jrange[jidx]] = i
4538-
iidx > length(rr) && continue
45394537
rotate_range = rr[iidx]:jrange[jidx]
4540-
circshift!(@view(vals[rotate_range]), -1)
4541-
circshift!(@view(rows[rotate_range]), -1)
4538+
circshift!(@view(vals[rotate_range]), 1)
4539+
circshift!(@view(rows[rotate_range]), 1)
45424540
end
45434541
end
45444542
return nothing

src/sparsevector.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1704,7 +1704,7 @@ for (fun, comp, word) in ((:findmin, :(<), "minimum"), (:findmax, :(>), "maximum
17041704
m == n && return val, index
17051705
nzinds = nonzeroinds(x)
17061706
zeroval = f(zero(T))
1707-
$comp(val, zeroval) && return val, nzinds[index]
1707+
($comp(val, zeroval) || isnan(val)) && return val, nzinds[index]
17081708
# we need to find the first zero, which could be stored or implicit
17091709
# we try to avoid findfirst(iszero, x)
17101710
sindex = findfirst(_iszero, nzvals) # first stored zero, if any

0 commit comments

Comments
 (0)