Skip to content
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

Closed
wants to merge 27 commits into from
Closed

pfaffian #35

wants to merge 27 commits into from

Conversation

smataigne
Copy link
Collaborator

first pfaffian.jl file

@smataigne
Copy link
Collaborator Author

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))
Copy link
Member

@stevengj stevengj Aug 12, 2022

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})
Copy link
Member

@stevengj stevengj Aug 12, 2022

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})
Copy link
Member

@stevengj stevengj Aug 12, 2022

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).
Copy link
Member

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)
Copy link
Member

@stevengj stevengj Aug 12, 2022

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.

Copy link
Member

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]
Copy link
Member

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.

Comment on lines +262 to +268
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
Copy link
Member

@stevengj stevengj Aug 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)

@stevengj
Copy link
Member

stevengj commented Aug 13, 2022

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 git push (you can do a git push --force if the branch was rebased). That way the discussion history is preserved and associated with the PR that was merged.

@smataigne smataigne closed this Aug 13, 2022
@smataigne smataigne deleted the smataigne branch August 24, 2022 14:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants