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

Feature Entanglement Fidelity Speedup and Bugfix #263

Merged
merged 7 commits into from
Sep 9, 2022

Conversation

coreyostrove
Copy link
Contributor

Some requests were made to look into making the calculation of the entanglement fidelity faster. This version speeds up the calculation when using TP process matrices, and when the target is unitary. Same formula as before for this case, but this uses the magic that is Numpy's einsum to calculate the trace by only constructing the diagonal of the requisite matrix product. In practice I found this to be 130-280X faster than the previous implementation, depending on the number of qubits.

I have also added optional flags that allow a user to manually specify whether the inputs are TP and unitary to short circuit the built in tests, which add time since the unitarity check at least involves doing a full matrix multiplication. Code still falls back to the built in tests when these flags aren't specified.

Also adds a minor bugfix to the TP check code to fix one of the ranges. This typo was causing the TP check to always fail which means that the fast implementation for the special case described above was never invoked and we were always falling back to the use of the Jamiolkowski isomorphism (which is orders of magnitude slower).

As a BOGO special I also used the same einsum trick to speedup the unitarity calculation. Not requested, but I noticed it'd apply there too.

Corey Ostrove added 2 commits September 8, 2022 00:13
Improves the performance of the entanglement fidelity calculation for the case where the process matrices are TP and the target it unitary by using the magic of einsum to only calculate the diagonal elements of a matrix we want to calculate the trace of.
The same trick we used to speed up the entanglement fidelity calculation can be used to speed up the unitarity calculation.
Copy link
Contributor

@sserita sserita left a comment

Choose a reason for hiding this comment

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

Overall looks good, but I think the docstring should be a little clearer given the dual-purpose nature of the new kwargs.

Also do we have any tests that cover this function? If not, can I bother you to add one?

@@ -427,7 +427,7 @@ def jtracedist(a, b, mx_basis='pp'): # Jamiolkowski trace distance: Tr(|J(a)-J
return tracedist(JA, JB)


def entanglement_fidelity(a, b, mx_basis='pp'):
def entanglement_fidelity(a, b, mx_basis='pp', is_tp_flag=None, is_unitary_flag=None):
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd rather have these kwargs be is_tp and is_unitary and we rename the helper functions below with a _fn suffix.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gotcha, I renamed the tp check function as suggested, but opted to inline the unitarity check (it was only called once in any case). Kwargs have also been updated as per your suggestion.

pygsti/tools/optools.py Show resolved Hide resolved
@@ -450,20 +450,51 @@ def entanglement_fidelity(a, b, mx_basis='pp'):
The basis of the matrices. Allowed values are Matrix-unit (std),
Gell-Mann (gm), Pauli-product (pp), and Qutrit (qt)
(or a custom basis object).

is_tp_flag : bool, optional (default None)
Copy link
Contributor

Choose a reason for hiding this comment

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

I originally didn't like the dual purpose of one variable to denote (i) whether to do the check and (ii) the value in lieu of the check, but I understand why you've done it this way. I think I'll be OK with it, so long as this docstring is a little more clear. Maybe something like:

Flag indicating both matrices are TP. If None (the default), an explicit check is performed. If True/False, the check is skipped and provided value is used (faster, but should only be used when the user is certain this is true apriori).

IDK exactly, but I want it to be clear that None is not just a dummy default - it actually changes the behavior of the function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great suggestion, wording was definitely unclear. I more or less verbatim copied your language directly since I thought it was very clear and concise.

Copy link
Collaborator

@enielse enielse left a comment

Choose a reason for hiding this comment

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

Great idea to do this, as it will definitely speed up the fidelity calcs as you've already attested. Just one possible "sign error" to check.

#Use einsum black magic to only calculate the diagonal elements
#if the basis is either pp or qm we know the elements are real-valued, so we
#don't need to take the conjugate
if mx_basis=='pp' or mx_basis=='qm':
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this line correct? From the comment, which jives with my understanding, .conjugate() can be dropped when the basis is 'pp' or 'qm', not added. I think this may be a "sign" error ;)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, while we're using einsum magic, we could swap ji to ij and drop the .T in the three einsum lines, e.g. _np.einsum('ij,ij->',a, b). This may be even slightly faster?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching that, they were indeed switched. Incidentally, I also had a typo in the basis label for the gell-mann matrices I hadn't noticed.

As for dropping the transpose, I just gave that a shot and found that it gives a 7% speedup for the 2-qubit case, but is within error bars of the version with the transpose for the 3-qubit case. Since numpy implements transposes using views, I'm guessing that the overhead of constructing that view is still measurable for 2-qubits, but is negligible for 3. Given that I'm going to opt to keep the transpose in for readability.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sounds good to me.

Corey Ostrove added 2 commits September 8, 2022 13:12
Fixes a typo where I had the inclusion of the conjugates switched and another typo where I mislabeled the basis key for the gell-man matrices.
Renames the kwargs for manually specifying if the input matrices are unitary and TP. Reworks the docstrings to make clear that the default value of None for the flags means that a built-in check is performed. Also in-lines the unitarity check (only used once and doesn't actually enhance readability).
@coreyostrove
Copy link
Contributor Author

RE: Unit tests. I'm not sure if there are currently, if not I'll add some.

@sserita
Copy link
Contributor

sserita commented Sep 8, 2022

There is one unit test that covers the general use case. It would be nice to add one or two more that cover the other code paths. They could be pretty simple: generate a test case that passes the TP/unitary condition, and compare the results of that against the function called with explicit is_tp/is_unitary=False to force it through the (known good) general use case. Similar thing could be done with the mx_basis by doing a change_basis to non-pp/gm basis (maybe std?) and comparing to the default pp version? Just some thoughts.

Adds a check for the special case when the inputs are TP and the target is unitary.
@coreyostrove
Copy link
Contributor Author

Got it, I've added a unit test for the TP/unitary case and can add some additional ones to cover changing the mx_basis and comparing the results for the special case to the jamiolkowski isomorphism.

Thoughts on adding the new kwargs to functions which call entanglement_fidelity as a sub-routine, to pass through? Skimming through that includes: average_gate_fidelity, average_gate_infidelity, entanglement_infidelity and gateset_infidelity (POVM fidelity does too, but the POVM "gates" are most certainly not TP or unitary).

Adds additional entanglement fidelity unit tests.
@coreyostrove
Copy link
Contributor Author

Added additional unit tests for all of the test cases I could think of.

@sserita
Copy link
Contributor

sserita commented Sep 8, 2022

This looks good to me. As for plumbing through the options, I think that sounds like a good idea but not essential. I'll leave the decision up to you - now that I've approved, I think you would be able to merge now if you didn't feel like putting in that effort, or you can add the extra plumbing and merge afterwards.

Copy link
Collaborator

@enielse enielse left a comment

Choose a reason for hiding this comment

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

Looks great to me. Nice work!

Plumbs the new entanglement fidelity kwargs to functions which call it as a subroutine.
@coreyostrove coreyostrove merged commit f81468e into develop Sep 9, 2022
@coreyostrove coreyostrove deleted the feature-entanglement-fid-speedup branch September 9, 2022 01:59
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.

3 participants