-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
pfaffian #35
Conversation
It seems that LinearAlgebra.exactdiv creates problems for some tests of git |
# update, A, c, n | ||
for j = 1:2n-2, i = 1:j-1 | ||
δ = A[2n-1,2n]*A[i,j] - A[i,2n-1]*A[j,2n] + A[j,2n-1]*A[i,2n] | ||
A[j,i] = -(A[i,j] = exactdiv(δ, c)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could just use div
since we restricted this to integers.
But the most general thing is to do:
if isdefined(LA, :exactdiv) # julia ≥ 1.7
const exactdiv = LA.exactdiv
else
exactdiv(a, b) = a / b
exactdiv(a::Integer, b::Integer) = div(a, b)
end
The exactdiv
function was introduced in Julia 1.7 for JuliaLang/julia#40868, which added an exact determinant algorithm for arbitrary rings via the Bareiss algorithm. However, I'm not 100% sure that the algorithm here works for arbitrary rings, and in particular it might implicitly assume that *
commutative. By default we should only use this for BigInt
(even Int
might overflow), but we can leave the function generic (but unexported) in case someone wants to call it (at their own risk), e.g. for Rational{BigInt}
.
# G. Galbiati & F. Maffioli, "On the computation of pfaffians," | ||
# Discrete Appl. Math. 51, 269–275 (1994). | ||
# https://doi.org/10.1016/0166-218X(92)00034-J | ||
function exactpfaffian!(A::AbstractMatrix{<:Integer}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also split it into multiple functions in order to support SkewHermitian
matrices more efficiently:
function _exactpfaffian!(A::AbstractMatrix)
... same as above, but with no argument checks ...
end
function exactpfaffian!(A::AbstractMatrix)
LinearAlgebra.require_one_based_indexing(A)
isskewhermitian(A) || throw(ArgumentError("Pfaffian requires a skew-Hermitian matrix"))
return _pfaffian!(A)
end
exactpfaffian!(A::SkewHermitian) = _pfaffian!(A.data)
pfaffian!(A::AbstractMatrix{<:BigInt}) = exactpfaffian!(A)
Note also that you will want an additional method somewhere for pfaffian(A::SkewHermTridiagonal{<:Real})
that just multiplies every other off-diagonal element.
exactpfaffian(A::AbstractMatrix{<:Integer}) = | ||
exactpfaffian!(copyto!(similar(A, typeof(exactdiv(zero(eltype(A))^2, one(eltype(A))))), A)) | ||
|
||
function realpfaffian(A::SkewHermitian{<:Real}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just call this pfaffian!
and have it call hessenberg!
Then have additional methods
pfaffian(A::AbstractMatrix{<:Real}) = pfaffian!(copy(A))
function pfaffian!(A::AbstractMatrix{<:Real})
isskewhermitian(A) || throw(...)
return pfaffian!(SkewHermitian(A))
end
using LinearAlgebra: exactdiv | ||
|
||
# in-place O(n³) algorithm to compute the exact Pfaffian of | ||
# a skew-symmetric matrix over integers (or potentially any ring supporting exact division). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change to commutative ring
@test pf*pf ≈ det(A) | ||
A=SLA.skewhermitian(randn(n,n)) | ||
pf= SLA.realpfaffian(A) | ||
@test pf*pf ≈ det(A.data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now you can check
Abig = BigInt.(A)
@test pfaffian(A) ≈ pfaffian(Abig) == pfaffian(SkewHermitian(Abig))
@test pfaffian(Abig)^2 == det(Abig)
and this will call the pfaffian!
methods etcetera.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also try a couple of random integer matrices with another package for the Pfaffian, and check that you can reproduce those (just hardcode the matrices and the results into the test) to make sure that you get the right sign.
@@ -257,14 +257,37 @@ end | |||
@test Matrix(HA.Q) ≈ Matrix(HB.Q) | |||
end | |||
end | |||
@testset "pfaffian.jl" begin | |||
for n in [2,4,6] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also check an odd case.
A=rand(1:10,n,n) | ||
for i=1:n | ||
A[i,i]=0 | ||
for j=i+1:n | ||
A[i,j]=-A[j,i] | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A=rand(1:10,n,n) | |
for i=1:n | |
A[i,i]=0 | |
for j=i+1:n | |
A[i,j]=-A[j,i] | |
end | |
end | |
A = skewhermitian!(rand(-10:10,n,n)*2) |
Looks like this PR can be closed, I guess it was superseded by another PR #47? In general, instead of opening new PRs, you should update an existing PR with |
first pfaffian.jl file