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

Improve reverse_bits to support registerless bits #7423

Merged
merged 11 commits into from
May 17, 2022

Conversation

yjt98765
Copy link
Contributor

@yjt98765 yjt98765 commented Dec 17, 2021

Summary

This PR fixes #7415. It modifies QuantumCircuit.reverse_bits to support circuits that contain registerless qubit and classical bits.

Details and comments

I assume the semantics of reverse_bits method is to reverse the order of registers but keep the bit order within a register unchanged. This assumption is made based on a test case of the function:

https://github.com/Qiskit/qiskit-terra/blob/6742941a62bf1a12c854f028dab15489118b6c15/test/python/circuit/test_circuit_operations.py#L878-L896

To emphasize these semantics, I modified the example circuit diagram in the docstring.

For registerless bits, I regard them as single-bit anonymous registers. Therefore, they are reversed. A test case is added to demonstrate this case. It would fail without the modification.

close #7415

@yjt98765 yjt98765 requested a review from a team as a code owner December 17, 2021 06:41
@yjt98765 yjt98765 marked this pull request as draft December 17, 2021 14:21
@coveralls
Copy link

coveralls commented Dec 19, 2021

Pull Request Test Coverage Report for Build 2339124462

  • 10 of 10 (100.0%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.02%) to 84.372%

Totals Coverage Status
Change from base Build 2338394703: 0.02%
Covered Lines: 54528
Relevant Lines: 64628

💛 - Coveralls

@yjt98765 yjt98765 marked this pull request as ready for review December 19, 2021 09:21
Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

Thank you for looking at this!

I've left some comments in line - I think on Terra we're still trying to transition from an old way of looking at registers (they used to be the most important thing) to a new model where bits are the most important, and registers are just a reference to a collection of bits. In particular, I made a comment about how I think this could be much easier just by constructing the circuit with the bit order reversed first, and then just remaking the registers later and adding them.

(new_cargs, cargs, self._clbit_indices),
]:
for arg in args:
_, regs = indices[arg]
Copy link
Member

Choose a reason for hiding this comment

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

This line could be registers = indices[arg].registers - in general with namedtuple it's better to use named access where possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. Thanks!

Comment on lines 503 to 517
for bits, indices in [
(self.qubits, self._qubit_indices),
(self.clbits, self._clbit_indices),
]:
i = len(bits) - 1
while i >= 0:
bit = bits[i]
_, regs = indices[bit]
if regs:
reg, local_idx = regs[0]
circ.add_register(reg)
i -= reg.size
else:
circ.add_bits([bit])
i -= 1
Copy link
Member

Choose a reason for hiding this comment

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

We also need to handle cases where registers overlap each other, i.e. the same bit is in multiple registers. I think the cleanest solution to this whole function will be (completely untested, so I could be wrong on the logic):

  1. create a circuit with no registers, and the same bit instances in reverse order:
    out = QuantumCircuit(
        reversed(self.qubits),
        reversed(self.clbits),
        name=self.name,
        global_phase=self.global_phase,
    )
  2. replay all the data from the old circuit into the new circuit without changing the args; because we reversed the bits, and the args all contain references to the bit instances, they'll automatically apply to the correct bits
  3. go through each register in the old circuit, and make a new register that references the correct bits in the new circuit

I think that's the correct logic, and it makes the correct separation between Bit and Register.

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 the suggestion. I followed these steps in the commit d0d8cc8.

Comment on lines 528 to 531
if regs:
reg, local_idx = regs[0]
arg = reg[reg.size - 1 - local_idx]
new_args.append(arg)
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 not sure why it's necessary to look in a register at this point?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was not aware of the overlap case. This part has been modified.

Comment on lines +899 to +917
def test_reverse_bits_with_registerless_bits(self):
"""Test reversing order of registerless bits."""
q0 = Qubit()
q1 = Qubit()
c0 = Clbit()
c1 = Clbit()
qc = QuantumCircuit([q0, q1], [c0, c1])
qc.h(0)
qc.cx(0, 1)
qc.x(0).c_if(1, True)
qc.measure(0, 0)

expected = QuantumCircuit([c1, c0], [q1, q0])
expected.h(1)
expected.cx(1, 0)
expected.x(1).c_if(0, True)
expected.measure(1, 1)

self.assertEqual(qc.reverse_bits(), expected)
Copy link
Member

Choose a reason for hiding this comment

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

This is a good test. Please could you also add some tests where there's a mix of registers and registerless bits, and some where there are some registers that overlap with each other?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added three additional test cases. They succeeded with the new logic. The downside may be that the way of constructing the expect circuit is not very straightforward, especially in the cases of overlapping bits.

@yjt98765
Copy link
Contributor Author

@jakelishman I have read your comments, including the one in #7415. I followed your steps and now can support both registerless qubits and converting little-endian to big-endian. Hope it works!

@javabster javabster added the help wanted community contributions welcome. For filters like http://github-help-wanted.com/ label Jan 26, 2022
@jakelishman jakelishman added this to the 0.21 milestone May 12, 2022
Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

Apologies this feel through the cracks. Thanks for this work - it looks good to me!

@jakelishman jakelishman added Changelog: Bugfix Include in the "Fixed" section of the changelog automerge and removed help wanted community contributions welcome. For filters like http://github-help-wanted.com/ labels May 17, 2022
@mergify mergify bot merged commit b898359 into Qiskit:main May 17, 2022
@yjt98765 yjt98765 deleted the hotfix-7415 branch May 17, 2022 15:26
ajavadia pushed a commit to ajavadia/qiskit that referenced this pull request May 31, 2022
* Improve reverse_bits to support registerless bits

* Change the algorithm of reverse_bits

* Reimplement reverse_bits with reversed bits

Also add more test cases.

* Simplify bit lookups

Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: Bugfix Include in the "Fixed" section of the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

QuantumCircuit.reverse_bits does not work with some circuits with registerless bits
4 participants