From 3e1a95bedf2b3257b56af42d203ae7903a4bf867 Mon Sep 17 00:00:00 2001 From: Pramodh Gopalan V Date: Sat, 22 May 2021 02:50:18 +0530 Subject: [PATCH] LinearAlgebra: Add bareiss det for BigInt Matrices (#40128) :: Take 2 (#40868) --- NEWS.md | 1 + stdlib/LinearAlgebra/src/generic.jl | 52 ++++++++++++++++++++++++++++ stdlib/LinearAlgebra/test/generic.jl | 5 +++ 3 files changed, 58 insertions(+) diff --git a/NEWS.md b/NEWS.md index 169ebd2fa742e9..27074440b3b492 100644 --- a/NEWS.md +++ b/NEWS.md @@ -108,6 +108,7 @@ Standard library changes * The shape of an `UpperHessenberg` matrix is preserved under certain arithmetic operations, e.g. when multiplying or dividing by an `UpperTriangular` matrix. ([#40039]) * `cis(A)` now supports matrix arguments ([#40194]). * `dot` now supports `UniformScaling` with `AbstractMatrix` ([#40250]). +* `det(M::AbstractMatrix{BigInt})` now calls `det_bareiss(M)`, which uses the [Bareiss](https://en.wikipedia.org/wiki/Bareiss_algorithm) algorithm to calculate precise values.([#40868]). #### Markdown diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index ec8e9df654c465..e0e7cebe2b0e1c 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1558,6 +1558,9 @@ function det(A::AbstractMatrix{T}) where T end det(x::Number) = x +# Resolve Issue #40128 +det(A::AbstractMatrix{BigInt}) = det_bareiss(A) + """ logabsdet(M) @@ -1620,6 +1623,55 @@ logdet(A) = log(det(A)) const NumberArray{T<:Number} = AbstractArray{T} +exactdiv(a, b) = a/b +exactdiv(a::Integer, b::Integer) = div(a, b) + +""" + det_bareiss!(M) + +Calculates the determinant of a matrix using the +[Bareiss Algorithm](https://en.wikipedia.org/wiki/Bareiss_algorithm) using +inplace operations. + +# Examples +```jldoctest +julia> M = [1 0; 2 2] +2×2 Matrix{Int64}: + 1 0 + 2 2 + +julia> LinearAlgebra.det_bareiss!(M) +2 +``` +""" +function det_bareiss!(M) + n = checksquare(M) + sign, prev = Int8(1), one(eltype(M)) + for i in 1:n-1 + if iszero(M[i,i]) # swap with another col to make nonzero + swapto = findfirst(!iszero, @view M[i,i+1:end]) + isnothing(swapto) && return zero(prev) + sign = -sign + Base.swapcols!(M, i, i + swapto) + end + for k in i+1:n, j in i+1:n + M[j,k] = exactdiv(M[j,k]*M[i,i] - M[j,i]*M[i,k], prev) + end + prev = M[i,i] + end + return sign * M[end,end] +end +""" + LinearAlgebra.det_bareiss(M) + +Calculates the determinant of a matrix using the +[Bareiss Algorithm](https://en.wikipedia.org/wiki/Bareiss_algorithm). +Also refer to [`det_bareiss!`](@ref). +""" +det_bareiss(M) = det_bareiss!(copy(M)) + + + """ promote_leaf_eltypes(itr) diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index 92baaa6363b4f6..cde16288f8f671 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -355,6 +355,11 @@ end @test [[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]] ≈ [[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]] end +@testset "Issue 40128" begin + @test det(BigInt[9 1 8 0; 0 0 8 7; 7 6 8 3; 2 9 7 7])::BigInt == -1 + @test det(BigInt[1 big(2)^65+1; 3 4])::BigInt == (4 - 3*(big(2)^65+1)) +end + # Minimal modulo number type - but not subtyping Number struct ModInt{n} k