Skip to content

Commit bd273ec

Browse files
committed
export symbolic/numeric factorization functions; add full_factor arg to constructor
1 parent 227db69 commit bd273ec

File tree

2 files changed

+76
-11
lines changed

2 files changed

+76
-11
lines changed

src/KLU.jl

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ module KLU
22

33
using SparseArrays
44
using SparseArrays: SparseMatrixCSC
5-
import SparseArrays: nnz
5+
import SparseArrays: nnz, nonzeros
66

77
export klu, klu!
8+
export klu_analyze!, klu_factor!, klu_refactor!, solve!
9+
export nonzeros
810

911
const libklu = :libklu
1012
include("wrappers.jl")
@@ -181,6 +183,15 @@ function size(K::AbstractKLUFactorization, dim::Integer)
181183
end
182184

183185
nnz(K::AbstractKLUFactorization) = K.lnz + K.unz + K.nzoff
186+
"""The nonzeros of the factorization `K`.
187+
!!! warning "`nonzeros(K)` may or may not point to `nonzeros(A)`"
188+
After `K = klu(A)`, it may or may not be the case that `nonzeros(K) === nonzeros(A)`.
189+
If `A` is of type `SparseMatrixCSC{Float32, Int64}`, then `nonzeros(F)` will be
190+
a `Vector{Float64}`, while `nonzeros(A)` is a `Vector{Float32}`, so they're definitely distinct.
191+
On the other hand, if `A` has value type `Float64`, then changing the values in `nonzeros(A)`
192+
will change `nonzeros(F)` as well.
193+
"""
194+
nonzeros(K::AbstractKLUFactorization) = K.nzval
184195

185196
if !isdefined(LinearAlgebra, :AdjointFactorization) # VERSION < v"1.10-"
186197
Base.adjoint(K::AbstractKLUFactorization) = Adjoint(K)
@@ -271,6 +282,7 @@ function getproperty(klu::AbstractKLUFactorization{Tv, Ti}, s::Symbol) where {Tv
271282
Lp = Vector{Ti}(undef, klu.n + 1)
272283
Li = Vector{Ti}(undef, lnz)
273284
Lx = Vector{Float64}(undef, lnz)
285+
# could also be replaced with multiple dispatch.
274286
Lz = Tv == Float64 ? C_NULL : Vector{Float64}(undef, lnz)
275287
_extract!(klu; Lp, Li, Lx, Lz)
276288
return Lp, Li, Lx, Lz
@@ -371,6 +383,8 @@ function show(io::IO, mime::MIME{Symbol("text/plain")}, K::AbstractKLUFactorizat
371383
end
372384
end
373385

386+
"""Compute and store (as `K._symbolic`) a symbolic factorization of the sparse matrix
387+
represented by the fields of `K`."""
374388
function klu_analyze!(K::KLUFactorization{Tv, Ti}; check=true) where {Tv, Ti<:KLUITypes}
375389
if K._symbolic != C_NULL return K end
376390
sym = __analyze(Ti, K.n, K.colptr, K.rowval, Ref(K.common))
@@ -382,7 +396,7 @@ function klu_analyze!(K::KLUFactorization{Tv, Ti}; check=true) where {Tv, Ti<:KL
382396
return K
383397
end
384398

385-
# User provided permutation vectors:
399+
"""Variant of `klu_analyze!` that allows for user-provided permutation permutation vectors `P` and `Q`."""
386400
function klu_analyze!(K::KLUFactorization{Tv, Ti}, P::Vector{Ti}, Q::Vector{Ti}; check=true) where {Tv, Ti<:KLUITypes}
387401
if K._symbolic != C_NULL return K end
388402
sym = __analyze!(K.n, K.colptr, K.rowval, P, Q, Ref(K.common))
@@ -517,6 +531,8 @@ The relation between `K` and `A` is
517531
- `check::Bool`: If `true` (default) check for errors after the factorization. If `false` errors must be checked by the user with `klu.common.status`.
518532
- `allowsingular::Bool`: If `true` (default `false`) allow the factorization to proceed even if the matrix is singular. Note that this will allow for
519533
silent divide by zero errors in subsequent `solve!` or `ldiv!` calls if singularity is not checked by the user with `klu.common.status == KLU.KLU_SINGULAR`
534+
- `full_factor::Bool`: if `true` (default), perform both numeric and symbolic factorization. If `false`, only perform symbolic factorization.
535+
Useful for cases where only the sparse structure of `A` is known at time of construction.
520536
521537
!!! note
522538
`klu(A::SparseMatrixCSC)` uses the KLU[^ACM907] library that is part of
@@ -526,31 +542,56 @@ The relation between `K` and `A` is
526542
527543
[^ACM907]: Davis, Timothy A., & Palamadai Natarajan, E. (2010). Algorithm 907: KLU, A Direct Sparse Solver for Circuit Simulation Problems. ACM Trans. Math. Softw., 37(3). doi:10.1145/1824801.1824814
528544
"""
529-
function klu(n, colptr::Vector{Ti}, rowval::Vector{Ti}, nzval::Vector{Tv}; check=true, allowsingular=false) where {Ti<:KLUITypes, Tv<:AbstractFloat}
545+
function klu(n,
546+
colptr::Vector{Ti},
547+
rowval::Vector{Ti},
548+
nzval::Vector{Tv};
549+
check=true,
550+
allowsingular=false,
551+
full_factor=true,
552+
) where {Ti<:KLUITypes, Tv<:AbstractFloat}
530553
if Tv != Float64
531554
nzval = convert(Vector{Float64}, nzval)
532555
end
533556
K = KLUFactorization(n, colptr, rowval, nzval)
534-
return klu_factor!(K; check, allowsingular)
557+
return full_factor ? klu_factor!(K; check, allowsingular) : klu_analyze!(K; check)
535558
end
536559

537-
function klu(n, colptr::Vector{Ti}, rowval::Vector{Ti}, nzval::Vector{Tv}; check=true, allowsingular=false) where {Ti<:KLUITypes, Tv<:Complex}
560+
function klu(n,
561+
colptr::Vector{Ti},
562+
rowval::Vector{Ti},
563+
nzval::Vector{Tv};
564+
check=true,
565+
allowsingular=false,
566+
full_factor=true,
567+
) where {Ti<:KLUITypes, Tv<:Complex}
538568
if Tv != ComplexF64
539569
nzval = convert(Vector{ComplexF64}, nzval)
540570
end
541571
K = KLUFactorization(n, colptr, rowval, nzval)
542-
return klu_factor!(K; check, allowsingular)
572+
return full_factor ? klu_factor!(K; check, allowsingular) : klu_analyze!(K; check)
543573
end
544574

545-
function klu(A::SparseMatrixCSC{Tv, Ti}; check=true, allowsingular=false) where {Tv<:Union{AbstractFloat, Complex}, Ti<:KLUITypes}
575+
function klu(A::SparseMatrixCSC{Tv, Ti};
576+
check=true,
577+
allowsingular=false,
578+
full_factor=true,
579+
) where {Tv<:Union{AbstractFloat, Complex}, Ti<:KLUITypes}
546580
n = size(A, 1)
547581
n == size(A, 2) || throw(DimensionMismatch())
548-
return klu(n, decrement(A.colptr), decrement(A.rowval), A.nzval; check, allowsingular)
582+
return klu(n,
583+
decrement(A.colptr),
584+
decrement(A.rowval),
585+
A.nzval;
586+
check,
587+
allowsingular,
588+
full_factor
589+
)
549590
end
550591

551592
"""
552593
klu!(K::KLUFactorization, A::SparseMatrixCSC; check=true, allowsingular=false) -> K::KLUFactorization
553-
klu(K::KLUFactorization, nzval::Vector{Tv}; check=true, allowsingular=false) -> K::KLUFactorization
594+
klu!(K::KLUFactorization, nzval::Vector{Tv}; check=true, allowsingular=false) -> K::KLUFactorization
554595
555596
Recompute the KLU factorization `K` using the values of `A` or `nzval`. The pattern of the original
556597
matrix used to create `K` must match the pattern of `A`.
@@ -576,7 +617,11 @@ See also: [`klu`](@ref)
576617
577618
[^ACM907]: Davis, Timothy A., & Palamadai Natarajan, E. (2010). Algorithm 907: KLU, A Direct Sparse Solver for Circuit Simulation Problems. ACM Trans. Math. Softw., 37(3). doi:10.1145/1824801.1824814
578619
"""
579-
function klu!(K::KLUFactorization{Tv, Ti}, nzval::Vector{Tv}; check=true, allowsingular=false) where {Tv<:KLUTypes, Ti<:KLUITypes}
620+
function klu!(K::KLUFactorization{Tv, Ti},
621+
nzval::Vector{Tv};
622+
check=true,
623+
allowsingular=false,
624+
) where {Tv<:KLUTypes, Ti<:KLUITypes}
580625
length(nzval) != length(K.nzval) && throw(DimensionMismatch())
581626
K.nzval = nzval
582627
K.common.halt_if_singular = !allowsingular && check
@@ -611,6 +656,12 @@ function klu!(K::KLUFactorization{U}, S::SparseMatrixCSC{U}; check=true, allowsi
611656
return klu!(K, S.nzval; check, allowsingular)
612657
end
613658

659+
"""
660+
klu_refactor!(...) -> K::KLUFactorization
661+
662+
Same as [`klu!`](@ref): alias for naming consistency reasons."""
663+
klu_refactor!(args...) = klu!(args...)
664+
614665
#B is the modified argument here. To match with the math it should be (klu, B). But convention is
615666
# modified first. Thoughts?
616667

test/runtests.jl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ using KLU
22
using KLU: increment!, KLUITypes, decrement, klu!, KLUFactorization,
33
klu_analyze!, klu_factor!
44
using Test
5-
using SparseArrays: SparseMatrixCSC, sparse, nnz
5+
using SparseArrays: SparseMatrixCSC, sparse, nnz, nonzeros
66
using LinearAlgebra
77

88
@testset "KLU Wrappers" begin
@@ -123,3 +123,17 @@ end
123123
@test issuccess(klu(A; allowsingular=true); allowsingular=true)
124124
end
125125
end
126+
127+
@testset "full_factor = false" begin
128+
for T in (Float64, ComplexF64, Float32, ComplexF32)
129+
A = sparse(Matrix{T}([1 0; 1 1]))
130+
nonzeros(A) .= zero(T)
131+
F = klu(A; full_factor = false)
132+
# we do need both here--for non 64 bit types, the nzval of F is not the same as A's.
133+
nonzeros(A) .= one(T)
134+
nonzeros(F) .= one(T)
135+
klu_factor!(F)
136+
b = ones(T, 2)
137+
@test F\b A\b
138+
end
139+
end

0 commit comments

Comments
 (0)