Skip to content

Commit ddd501b

Browse files
committed
LinearAlgebra.det improvements
Still broken for `LinearAlgebra.Symmetric` polynomial matrices, producing a `MethodError` because of a missing `oneunit` method. This, however, seems like a separate matter that would better be addressed by a separate pull request. Performance comparison: ``` julia> versioninfo() Julia Version 1.11.0-DEV.972 Commit 9884e447e79 (2023-11-23 16:16 UTC) Build Info: Official https://julialang.org/ release Platform Info: OS: Linux (x86_64-linux-gnu) CPU: 8 × AMD Ryzen 3 5300U with Radeon Graphics WORD_SIZE: 64 LLVM: libLLVM-15.0.7 (ORCJIT, znver2) Threads: 11 on 8 virtual cores julia> using LinearAlgebra, DynamicPolynomials julia> @PolyVar a b c d e; julia> f(n) = diagm( -2 => fill(a, n - 2), -1 => fill(b, n - 1), 0 => fill(c, n), 2 => fill(e, n - 2), 1 => fill(d, n - 1), ) f (generic function with 1 method) julia> const m15 = f(15); julia> const m16 = f(16); julia> @time det(m15); 2.849449 seconds (47.98 M allocations: 1.832 GiB, 16.50% gc time, 11.14% compilation time) julia> @time det(m15); 2.832748 seconds (47.94 M allocations: 1.830 GiB, 22.05% gc time) julia> @time det(m16); 6.476343 seconds (112.61 M allocations: 4.299 GiB, 20.43% gc time) julia> @time det(m16); 6.737904 seconds (112.61 M allocations: 4.299 GiB, 22.30% gc time) ``` The above REPL session is with this commit applied. The same computation with MultivariatePolynomials v0.5.3 ran for multiple minutes before I decided to just kill it. Fixes #281
1 parent fcb1b29 commit ddd501b

File tree

1 file changed

+61
-10
lines changed

1 file changed

+61
-10
lines changed

src/det.jl

+61-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,64 @@
1-
function LinearAlgebra.det(M::Matrix{<:AbstractPolynomialLike})
2-
m = size(M)[1]
3-
if m > 2
4-
return sum(
5-
(-1)^(i - 1) * M[i, 1] * LinearAlgebra.det(M[1:end.!=i, 2:end]) for
6-
i in 1:m
7-
)
8-
elseif m == 2
9-
return M[1, 1] * M[2, 2] - M[2, 1] * M[1, 2]
1+
# Scalar determinant, used for recursive computation of the determinant
2+
LinearAlgebra.det(p::AbstractPolynomialLike{<:Number}) = p
3+
4+
# Matrix determinant by cofactor expansion, adapted from
5+
# `LinearAlgebraX.cofactor_det`.
6+
function det_impl(A::AbstractMatrix{T}) where {T}
7+
r = (first size)(A)
8+
if 1 < r
9+
# TODO: use MutableArithmetics.jl for performance
10+
total = (LinearAlgebra.det zero)(T)
11+
for i Base.OneTo(r)
12+
a = LinearAlgebra.det(A[i, 1])
13+
if !iszero(a)
14+
ii = Base.OneTo(r) .!= i
15+
jj = 2:r
16+
B = A[ii, jj]
17+
x = a * det_impl(B)
18+
if iseven(i)
19+
total -= x
20+
else
21+
total += x
22+
end
23+
end
24+
end
25+
total
26+
elseif isone(r)
27+
LinearAlgebra.det(A[1, 1])
1028
else
11-
return M[1, 1]
29+
error("unexpected")
1230
end
1331
end
32+
33+
collect_if_not_already_matrix(m::Matrix) = m
34+
collect_if_not_already_matrix(m::AbstractMatrix) = collect(m)
35+
36+
function det_impl_outer(m::AbstractMatrix{T}) where {T}
37+
if 0 < LinearAlgebra.checksquare(m)
38+
(det_impl collect_if_not_already_matrix)(m)
39+
else
40+
(LinearAlgebra.det one)(T)
41+
end
42+
end
43+
44+
# Determinants of narrow integer type: `LinearAlgebra` seems to
45+
# promote these to `Float64` to prevent them from overflowing. We
46+
# instead promote to `BigInt` to keep things exact. In the case of
47+
# `Bool` we also need to promote for type stability.
48+
49+
const DeterminantNarrowIntegerTypes = Union{
50+
Bool, UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt64, Int64,
51+
UInt128, Int128,
52+
}
53+
54+
const NarrowIntegerPolynomialLike =
55+
AbstractPolynomialLike{T} where {T<:DeterminantNarrowIntegerTypes}
56+
57+
promote_if_narrow(m::AbstractMatrix{<:AbstractPolynomialLike}) = m
58+
59+
# TODO: avoid the unnecessary multiplication
60+
promote_if_narrow(m::AbstractMatrix{<:NarrowIntegerPolynomialLike}) =
61+
m * one(BigInt)
62+
63+
LinearAlgebra.det(m::AbstractMatrix{<:AbstractPolynomialLike}) =
64+
(det_impl_outer promote_if_narrow)(m)

0 commit comments

Comments
 (0)