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

Add CX cancellation pass #24

Merged
merged 8 commits into from
Sep 25, 2024
Merged

Add CX cancellation pass #24

merged 8 commits into from
Sep 25, 2024

Conversation

Misty-W
Copy link
Collaborator

@Misty-W Misty-W commented Sep 22, 2024

Fixes #17.

Adds a custom Qiskit transpiler pass to cancel redundant CX gates, according to commutation rules summarized here: https://www.aspdac.com/aspdac2019/archive/pdf/2D-2.pdf, swapping commuting gates, and using Qiskit functions to check whether the gates are inverses of each other.

As noted in the file itself, the added file is a derivative work from related transpiler passes in Qiskit. The improvement is in using the commutation information obtained to switch the gates, check inverses, and perform cancellations where applicable.

Copy link
Member

@natestemen natestemen left a comment

Choose a reason for hiding this comment

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

Cool to see what a transformation pass looks like on Qiskit's DAG! Thanks for letting me take a look. Some questions mostly :)

custom_cx.py Outdated Show resolved Hide resolved
custom_cx.py Outdated Show resolved Hide resolved
custom_cx.py Outdated Show resolved Hide resolved
custom_cx.py Outdated Show resolved Hide resolved
custom_cx.py Outdated Show resolved Hide resolved
custom_cx.py Outdated Show resolved Hide resolved
custom_cx.py Outdated
Comment on lines 91 to 92
for idx1 in range(0, circ_size):
for idx2 in range(idx1 - 1, -1, -1):
Copy link
Member

@natestemen natestemen Sep 23, 2024

Choose a reason for hiding this comment

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

Just checking my understanding. You want to loop over every node, and compare it with every node that has come prior, right? If it doesn't matter if you're looking forward or backward then this could also be simplified to loop over the nodes directly, instead of indices.

for i, node1 in enumerate(topo_sorted_nodes):
    for node2 in topo_sorted_nodes[i+1:]:

Question: Suppose we have a circuit, which at some point has gates $\cdots ABC\cdots$ where $A$ does not commute with $B$ and $B$ does not commute with $C$. Is $B$ checked for inverse/commutation against every other gate in the circuit? If so this is likely very expensive!


Another small thing: idx1 == 0 is not used since the range computed on the line below doesn't yield anything (i.e. list(range(-1, -1, -1)) == []).

Copy link
Collaborator Author

@Misty-W Misty-W Sep 24, 2024

Choose a reason for hiding this comment

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

Yes, you have the right idea, and I agree looping over the nodes directly is better. I also agree that this is not the most efficient approach in the long term, but it is the same approach as our baseline, i.e. what the Qiskit commutation checker uses. The improvement is that we use the commutation information obtained to switch the gates, check inverses, and perform cancellations where applicable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Cool, let's create a new issue for optimizing this and we can tackle it in the future.

@natestemen
Copy link
Member

Apologies for the "outdated" comments. Looks like I started reviewing and a file got moved mid-way through.

@jordandsullivan jordandsullivan removed their assignment Sep 24, 2024
@Misty-W
Copy link
Collaborator Author

Misty-W commented Sep 24, 2024

thanks for the review @natestemen! no worries about the outdate comments, that was on me for moving things around.

ucc/custom_cx.py Outdated Show resolved Hide resolved
Co-authored-by: nate stemen <[email protected]>
Copy link
Member

@natestemen natestemen left a comment

Choose a reason for hiding this comment

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

It's not clear to me that the commutation rules are being applied in a way that's useful here. E.g. I don't think the following situation (which is the simplest I can imagine) is being found/optimized.

Suppose you have a circuit defined as $C = \mathtt{CNOT}_{1,2} \cdot U \cdot \mathtt{CNOT}_{1,2}$ where $U\in\mathsf{U}(4)$ commutes with $\mathtt{CNOT}$. In order to compile this circuit to $C = U$, you need to

  1. check if $U$ commutes with one of the $\mathtt{CNOT}$ gates
  2. commute the gates (i.e. $C = U \cdot \mathtt{CNOT}_{1,2} \cdot \mathtt{CNOT}_{1,2}$)
  3. perform a round of inverse cancellations ($C = U$)

As my understanding goes (and I might be missing some magic that is performed on behalf of the qiskit transpiler pipeline), we are only checking if gates next to each other are inverses. There is a commutation check, but gates are never actually swapped to be checked against their new neighbors.

Comment on lines +100 to +111
if self.commute(
node1.op,
node1.qargs,
node2.op,
node2.qargs
):
node1, node2 = node2, node1
is_inverse, phase_update = self._check_inverse(node1, node2)
if is_inverse:
dag._multi_graph.remove_node_retain_edges_by_id(node1._node_id)
dag._multi_graph.remove_node_retain_edges_by_id(node2._node_id)
dag.global_phase += phase_update
Copy link
Member

Choose a reason for hiding this comment

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

I'm confused why this block is needed. I would expect _check_inverse(node1, node2) == _check_inverse(node2, node1) which would make this second block redundant. What am I missing here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I wasn't sure at first, but the first inverse check is redundant- another holdover from the Qiskit commutation checker. I removed it and tested again, and it still works.

@jordandsullivan jordandsullivan merged commit b50bfe9 into main Sep 25, 2024
@natestemen
Copy link
Member

natestemen commented Sep 25, 2024

I know this has been merged now, but my comment regarding commutation not being used is still open (at least to me). As far as I understand, the code only implements nearest neighbor inverse checks, which is different from the issue and PR description goals.

@jordandsullivan
Copy link
Collaborator

I know this has been merged now, but my comment regarding commutation not being used is still open (at least to me). As far as I understand, the code only implements nearest neighbor inverse checks, which is different from the issue and PR description goals.

Are you able to produce a code snippet that demonstrates what the code is failing to do @nathanshammah? Do you have something different in mind than what's in this test: https://github.com/unitaryfund/ucc/blob/main/ucc/transpiler_passes/tests/test_cx_cancellation.py

@natestemen
Copy link
Member

Yup! Here's a commit b7ef8e9 with a new test that shows that the $R_z$-control rule1 is not being applied.

BTW, reminder that I'm nate, not Nathan 😛.

Footnotes

  1. Defined as the first example on page 10 of https://www.aspdac.com/aspdac2019/archive/pdf/2D-2.pdf.

@jordandsullivan
Copy link
Collaborator

Agh my brain is broken this week @natestemen I am so sorry XD
I owe you a birthday card with your name written on it correctly! Thank you very much for creating that example. :)

@Misty-W does this help clear up what may be happening in your open PR #36 ?

@Misty-W
Copy link
Collaborator Author

Misty-W commented Sep 27, 2024

@jordandsullivan and @natestemen, I believe I understand the issue and will try to fix it in PR #36. I need to be careful with the indexing, but if I get that right, it should work.

@Misty-W Misty-W deleted the mw-custom-cx-experiments branch September 27, 2024 01:03
jordandsullivan added a commit that referenced this pull request Nov 13, 2024
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.

Improve CX cancellation
3 participants