Skip to content

Commit 5e306ed

Browse files
authored
make \ type stable (#684)
Closes #111 Since UMFPACK is not going to add support for `Float32` anytime soon I think the only solution is to manually convert back. Also, since the code for dealing with the output of `lu` is not generic, there is no point in converting back the decomposition itself, so I'm just converting the result of `lu(A) \ b` to the appropriate type. I also did the analogous change to achieve type stability with `Float16`. There's also nothing that can be done about CHOLMOD, but for SPQR the situation is better: the code around it is generic, so I just converted back `qr(A)` itself.
1 parent 41cc138 commit 5e306ed

6 files changed

Lines changed: 32 additions & 19 deletions

File tree

src/linalg.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2252,7 +2252,7 @@ function \(A::AbstractSparseMatrixCSC, B::AbstractVecOrMat)
22522252
if ishermitian(A)
22532253
return \(Hermitian(A), B)
22542254
end
2255-
return \(lu(A), B)
2255+
return convert(AbstractArray{typeof(one(eltype(A)) \ one(eltype(B)))}, \(lu(A), B))
22562256
else
22572257
return \(qr(A), B)
22582258
end

src/solvers/cholmod.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,14 +1899,15 @@ end
18991899
const RealHermSymComplexHermSSL{Ti, Tr} = Union{
19001900
Symmetric{Tr, SparseMatrixCSC{Tr, Ti}},
19011901
Hermitian{Tr, SparseMatrixCSC{Tr, Ti}},
1902-
Hermitian{Complex{Tr}, SparseMatrixCSC{Complex{Tr}, Ti}}} where {Ti<:ITypes, Tr<:VRealTypes}
1902+
Hermitian{Complex{Tr}, SparseMatrixCSC{Complex{Tr}, Ti}}} where {Ti<:ITypes, Tr<:Union{Float64, Float32, Float16}}
19031903

19041904
function \(A::RealHermSymComplexHermSSL{Ti}, B::StridedVecOrMatInclAdjAndTrans) where {Ti}
1905+
T = typeof(one(eltype(A)) \ one(eltype(B)))
19051906
F = cholesky(A; check = false)
19061907
if issuccess(F)
1907-
return \(F, B)
1908+
return convert(AbstractArray{T}, \(F, B))
19081909
else
1909-
return \(lu(SparseMatrixCSC{eltype(A), Ti}(A)), B)
1910+
return convert(AbstractArray{T}, \(lu(SparseMatrixCSC{eltype(A), Ti}(A)), B))
19101911
end
19111912
end
19121913

src/solvers/spqr.jl

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ function _qr!(ordering::Integer, tol::Real, econ::Integer, getCTX::Integer,
108108
return rnk, _E, _HPinv
109109
end
110110

111-
struct QRSparseQ{Tv<:CHOLMOD.VTypes,Ti<:Integer} <: AbstractQ{Tv}
111+
struct QRSparseQ{Tv,Ti<:Integer} <: AbstractQ{Tv}
112112
factors::SparseMatrixCSC{Tv,Ti}
113113
τ::Vector{Tv}
114114
n::Int # Number of columns in original matrix
@@ -134,6 +134,14 @@ struct QRSparse{Tv,Ti} <: LinearAlgebra.Factorization{Tv}
134134
_ldiv_workspace::Vector{Tv} # backing storage for work buffer (resizable)
135135
end
136136

137+
function QRSparse{Tv}(F::QRSparse{<:Number, Ti}) where {Tv, Ti}
138+
newfactors = convert(SparseMatrixCSC{Tv}, F.factors)
139+
newτ = convert(Vector{Tv}, F.τ)
140+
newR = convert(SparseMatrixCSC{Tv}, F.R)
141+
newQ = QRSparseQ{Tv,Ti}(newfactors, newτ, size(newR, 2))
142+
return QRSparse{Tv,Ti}(newfactors, newτ, newR, newQ, F.cpiv, F.rpivinv, ReentrantLock(), Tv[])
143+
end
144+
137145
Base.size(F::QRSparse) = (size(F.factors, 1), size(F.R, 2))
138146
function Base.size(F::QRSparse, i::Integer)
139147
if i == 1
@@ -167,9 +175,9 @@ solve least squares or underdetermined problems with [`\\`](@ref). The function
167175
168176
!!! note
169177
`qr(A::SparseMatrixCSC)` uses the SPQR library that is part of [SuiteSparse](https://github.com/DrTimothyAldenDavis/SuiteSparse).
170-
As this library only supports sparse matrices with [`Float64`](@ref) or
171-
`ComplexF64` elements, as of Julia v1.4 `qr` converts `A` into a copy that is
172-
of type `SparseMatrixCSC{Float64}` or `SparseMatrixCSC{ComplexF64}` as appropriate.
178+
As this library only supports sparse matrices with [`Float64`](@ref), `ComplexF64`, `Float32`, or
179+
`ComplexF32` elements, calling `qr` on a matrix with a different element type will either convert it to a supported type or
180+
raise an error.
173181
174182
# Examples
175183
```jldoctest
@@ -230,9 +238,9 @@ function LinearAlgebra.qr(A::SparseMatrixCSC{Tv, Ti}; tol=_default_tol(A), order
230238
Tv[]) # _ldiv_workspace (lazily sized on first solve)
231239
end
232240
LinearAlgebra.qr(A::SparseMatrixCSC{Float16}; tol=_default_tol(A)) =
233-
qr(convert(SparseMatrixCSC{Float32}, A); tol=tol)
241+
QRSparse{Float16}(qr(convert(SparseMatrixCSC{Float32}, A); tol=tol))
234242
LinearAlgebra.qr(A::SparseMatrixCSC{ComplexF16}; tol=_default_tol(A)) =
235-
qr(convert(SparseMatrixCSC{ComplexF32}, A); tol=tol)
243+
QRSparse{ComplexF16}(qr(convert(SparseMatrixCSC{ComplexF32}, A); tol=tol))
236244
LinearAlgebra.qr(A::Union{SparseMatrixCSC{T},SparseMatrixCSC{Complex{T}}};
237245
tol=_default_tol(A)) where {T<:AbstractFloat} =
238246
throw(ArgumentError(string("matrix type ", typeof(A), "not supported. ",

test/cholmod.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,9 +853,9 @@ end
853853
Symmetric(Apre + 10I), Hermitian(Apre + 10I),
854854
Hermitian(complex(Apre)), Hermitian(complex(Apre) + 10I))
855855
local A, x, b
856-
x = fill(1., 10)
856+
x = fill(1, 10)
857857
b = A*x
858-
@test x A\b
858+
@test @inferred A\b x
859859
@test transpose(A)\b A'\b
860860
end
861861
end

test/linalg.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,4 +1040,14 @@ end
10401040
@test_throws DimensionMismatch D1 * S * D1
10411041
end
10421042

1043+
@testset "type stability of linear solve" begin
1044+
for relty in (Float16, Float32, Float64), elty in (relty, Complex{relty})
1045+
A = sprand(elty, 2, 2, 1.0)
1046+
B = randn(elty, 2, 2)
1047+
b = randn(elty, 2)
1048+
@inferred A \ b
1049+
@inferred A \ B
1050+
end
1051+
end
1052+
10431053
end

test/spqr.jl

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,12 @@ end
110110
@test (F.Q*F.R)::SparseMatrixCSC == A[F.prow,F.pcol]
111111
end
112112

113-
@testset "Issue #585 for element type: $eltyA" for eltyA in (Float64, Float32, ComplexF64, ComplexF32)
113+
@testset "Issue #585 for element type: $eltyA" for eltyA in (Float64, Float32, Float16, ComplexF64, ComplexF32, ComplexF16)
114114
A = sparse(eltyA[1 0; 0 1])
115115
F = qr(A)
116116
@test eltype(F.Q) == eltype(F.R) == eltyA
117117
end
118118

119-
@testset "Complementing issue #585 for element type: $eltyA" for (eltyA, eltyB) in [(Float16, Float32), (ComplexF16, ComplexF32)]
120-
A = sparse(eltyA[1 0; 0 1])
121-
F = qr(A)
122-
@test eltype(F.Q) == eltype(F.R) == eltyB
123-
end
124-
125119
@testset "select ordering overdetermined" begin
126120
A = sparse([1:n; rand(1:m, nn - n)], [1:n; rand(1:n, nn - n)], randn(nn), m, n)
127121
b = randn(m)

0 commit comments

Comments
 (0)