-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Fix Hoare optimizer failure for successive gates of three #7278
Conversation
Pull Request Test Coverage Report for Build 1508471456
💛 - Coveralls |
What if instead you move the condition, if self._is_identity(seq) and self._seq_as_one(seq): to new_seq = [node1, node2]
if append:
if self._is_identity(new_seq) and self._seq_as_one(new_seq):
if not (seqs and set(new_seq) & set(seqs[-1])):
seqs.append(new_seq) This way you only have to check the last added pair. |
Yes, it seems better. Maybe the whole block can be moved to
It is not related to the other parts of |
@yjt98765 That sounds good. |
@ewinston I have updated the code. By moving that block to |
@yjt98765 Thanks for the fix! |
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.
Thanks for the fix, and sorry for the delay! I had a couple more comments down below - don't worry about the bit that says it's out-of-scope, that's just for history purposes. Please could you also add a release note describing the bugfix as well?
max_idx (int): a value indicates a recursive call, optimize | ||
and remove gates up to this point in the cache |
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.
This documentation is a bit confusing now - _target_successive_seq
doesn't call itself recursively.
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 see your point here: the moved code/comment should make sense in the new context. I have updated the docstring. In addition, max_idx
is a bit misleading here, so I renamed it to from_idx
.
# if recursive call, gate will be removed further down | ||
if max_idx is None: | ||
for qbt in node.qargs: | ||
self.gatecache[qbt].remove(node) | ||
else: | ||
if self.gatecache[qubit].index(node) > max_idx: | ||
for qbt in node.qargs: | ||
self.gatecache[qbt].remove(node) |
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 similar comment here - _remove_successive_identity
isn't a recursive function.
Also, I know this was pre-existing, but this whole logic block is equivalent to
if max_idx is None or self.gatecache[qubit].index(node) > max_idx:
for qbt in node.qargs:
self.gatecache[qbt].remove(node)
which avoids an extra scope, and removes some duplication.
(A comment far beyond the scope of the PR: this pre-existing code smells of poor scaling - list.index
and list.remove
are very tricky to use well, and this is highly likely to be at least quadratic in the circuit depth.)
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.
Indeed, I have merged the code as you suggested here.
@jakelishman sure, I have added a release note. |
I pushed a commit just to reword the release note a little - mostly I wanted to avoid referring to a private function in it, because users might not know what that meant. I was about to merge this, but then I realised I'm a bit confused - the |
@jakelishman I am not the author of the original program, but I guess the key to understanding the parameter is the last line of the parent function
The variable |
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.
Thanks - it seems ok to me to leave it as it is, then.
Summary
This PR fixes the bug reported by #7271.
When three H gates apply on the same qubit successively, the H gate in the middle would be deleted twice by the Hoare optimizer. It causes an exception.
Details and comments
The function
_multigate_opt
examines the sequences of successive gates generated by_target_successive_seq
. If a sequence combines to the identity, all the gates in the sequence are removed. It works for most cases except three successive H gates. See this example proposed by @ewinston:I will use
H1
,H2
, andH3
to denote theDAGOpNode
corresponding to the three H gates. The sequences generated by_target_successive_seq
regardingqubit[0]
is[[H1, H2], [H2, H3]]
. After analyzing the first sequence ([H1, H2]
), the optimizer deletedH1
andH2
. After analyzing the second sequence ([H2, H3]
), the optimizer tried to delete these two gates and found that the key related toH2
does not exist ().To resolve this issue, I have moved the gate-deleting code from
_multigate_opt
to_target_successive_seq
. When two gates are removed, the iteration index adds an extra 1, so that the deleted gate will not be considered in the next iteration. Since the function of_target_successive_seq
has changed, I renamed it as_remove_successive_identity
.I have added a test case to demonstrate the change. The test case is adapted based on @ewinston 's example and existing test cases. Before the change, the test case would fail with a
KeyError
. It passes after the modifications.Close #7271