Skip to content

Implement random unitary matrices #40381

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

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

orlitzky
Copy link
Contributor

@orlitzky orlitzky commented Jul 7, 2025

Add a new random_unitary_matrix() function that is called from matrix.random(..., algorithm='unitary').

@orlitzky orlitzky marked this pull request as draft July 7, 2025 01:26
I'm getting a few warnings about tests in this file that take longer
than 5s to run (without --long). Here we add "# long time" tags.
@orlitzky orlitzky marked this pull request as ready for review July 7, 2025 10:44
@orlitzky orlitzky requested a review from JohnCremona July 7, 2025 10:45
@orlitzky
Copy link
Contributor Author

orlitzky commented Jul 7, 2025

@JohnCremona you are mentioned by name in the paper :)

The CI, lint, and docs all look broken in the latest develop branch but none of it looks related this this PR. I have not been able to check that the docs look OK however.

@JohnCremona
Copy link
Member

@JohnCremona you are mentioned by name in the paper :)

I guess that will be referring to a letter to the editor of the Americal Mathematical Monthly which I wrote on 23 Feb 1987!
AMM_letter.pdf

S = A - A.conjugate_transpose()
U = (S-I).inverse()*(S+I)
D = identity_matrix(F,n)
for i in range(n):
Copy link
Member

Choose a reason for hiding this comment

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

Why create D, you could reuse I?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(This code has been sitting in my personal library for a while.) I was probably in a mood where I considered it a sin to have a variable named I that was no longer the identity matrix.

Regardless, I've replaced the diagonal \pm 1 matrix by a loop that uses U.set_row_to_multiple_of_row, so now it's irrelevant. The new method is written in cython so it should be faster anyway. Thanks for the suggestion.

U = (S-I).inverse()*(S+I)
D = identity_matrix(F,n)
for i in range(n):
if ZZ.random_element(2).is_zero():
Copy link
Member

Choose a reason for hiding this comment

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

Is this really the best way to toss a coin? random()<0.5 would be as good?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably an irrational fear that my 50/50 probability will be wrong by the unit roundoff. I've replaced it. random() will definitely be faster.

D = identity_matrix(F,n)
for i in range(n):
if ZZ.random_element(2).is_zero():
D[i,i] *= F(-1)
Copy link
Member

Choose a reason for hiding this comment

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

It seems crazy that field elements are not required to have a negate() mthod, but apparently not. But they do have a method called neg() with double underscores for evaluatio of unary minus.

I don't think it is necessary to construct F(-1) every time through the loop. Perhaps D[i,i] = -D[i,i] is better?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is handled within set_row_to_multiple_of_row() now (it casts the -1 to a rational for the flint API).

Copy link
Member

@JohnCremona JohnCremona left a comment

Choose a reason for hiding this comment

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

This looks good. I made some trivial coding comments. Can you briefly say why you randomly negate rows (and is this the best way)?

@orlitzky
Copy link
Contributor Author

orlitzky commented Jul 7, 2025

This looks good. I made some trivial coding comments. Can you briefly say why you randomly negate rows (and is this the best way)?

It matched the paper closely but was definitely not the best way. I've improved it, and have mentioned why we are negating some rows. Thanks for the review!

@JohnCremona
Copy link
Member

positive review if the failing tests are irrelevant

orlitzky added 3 commits July 7, 2025 15:05
New reference (Liebeck and Osborne, 1991) to be used in a new method
for generating random unitary matrices.
Add a new function to generate random unitary matrices based on the
paper "The Generation of All Rational Orthogonal Matrices" by Liebeck
and Osborne (1991).
Use the lower-level methods Matrix.set_row_to_multiple_of_row() and
random.random() to implement random_unitary_matrix(). This should
bring with it a small speed increase, and actually makes the code
simpler. Thanks to John Cremona for the suggestion.
@orlitzky
Copy link
Contributor Author

orlitzky commented Jul 7, 2025

Force push to fix the tab character in the reference file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants