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
50 changes: 32 additions & 18 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,37 +475,51 @@ def reverse_bits(self) -> "QuantumCircuit":
.. parsed-literal::

┌───┐
q_0: ┤ H ├─────■──────
└───┘┌────┴─────┐
q_1: ─────┤ RX(1.57) ├
└──────────┘
a_0: ┤ H ├──■─────────────────
└───┘┌─┴─┐
a_1: ─────┤ X ├──■────────────
└───┘┌─┴─┐
a_2: ──────────┤ X ├──■───────
└───┘┌─┴─┐
b_0: ───────────────┤ X ├──■──
└───┘┌─┴─┐
b_1: ────────────────────┤ X ├
└───┘

output:

.. parsed-literal::

┌──────────┐
q_0: ─────┤ RX(1.57) ├
┌───┐└────┬─────┘
q_1: ┤ H ├─────■──────
┌───┐
b_0: ────────────────────┤ X ├
┌───┐└─┬─┘
b_1: ───────────────┤ X ├──■──
┌───┐└─┬─┘
a_0: ──────────┤ X ├──■───────
┌───┐└─┬─┘
a_1: ─────┤ X ├──■────────────
┌───┐└─┬─┘
a_2: ┤ H ├──■─────────────────
└───┘
"""
circ = QuantumCircuit(
*reversed(self.qregs),
*reversed(self.cregs),
list(reversed(self.qubits)),
list(reversed(self.clbits)),
name=self.name,
global_phase=self.global_phase,
)
num_qubits = self.num_qubits
num_clbits = self.num_clbits
old_qubits = self.qubits
old_clbits = self.clbits
new_qubits = circ.qubits
new_clbits = circ.clbits
new_qubit_map = circ.qubits[::-1]
new_clbit_map = circ.clbits[::-1]
for reg in reversed(self.qregs):
bits = [new_qubit_map[self.find_bit(qubit).index] for qubit in reversed(reg)]
circ.add_register(QuantumRegister(bits=bits, name=reg.name))
for reg in reversed(self.cregs):
bits = [new_clbit_map[self.find_bit(clbit).index] for clbit in reversed(reg)]
circ.add_register(ClassicalRegister(bits=bits, name=reg.name))

for inst, qargs, cargs in self.data:
new_qargs = [new_qubits[num_qubits - old_qubits.index(q) - 1] for q in qargs]
new_cargs = [new_clbits[num_clbits - old_clbits.index(c) - 1] for c in cargs]
new_qargs = [new_qubit_map[self.find_bit(qubit).index] for qubit in qargs]
new_cargs = [new_clbit_map[self.find_bit(clbit).index] for clbit in cargs]
circ._append(inst, new_qargs, new_cargs)
return circ

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
fixes:
- |
Fixed :meth:`.QuantumCircuit.reverse_bits` with circuits containing registerless
:class:`.Qubit` and :class:`.Clbit`. For example, the following will now work::

from qiskit.circuit import QuantumCircuit, Qubit, Clbit

qc = QuantumCircuit([Qubit(), Clbit()])
qc.h(0).c_if(qc.clbits[0], 0)
qc.reverse_bits()
74 changes: 74 additions & 0 deletions test/python/circuit/test_circuit_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,80 @@ def test_reverse_bits_with_registers(self):

self.assertEqual(qc.reverse_bits(), expected)

def test_reverse_bits_with_overlapped_registers(self):
"""Test reversing order of bits when registers are overlapped."""
qr1 = QuantumRegister(2, "a")
qr2 = QuantumRegister(bits=[qr1[0], qr1[1], Qubit()], name="b")
qc = QuantumCircuit(qr1, qr2)
qc.h(qr1[0])
qc.cx(qr1[0], qr1[1])
qc.cx(qr1[1], qr2[2])

qr2 = QuantumRegister(bits=[Qubit(), qr1[0], qr1[1]], name="b")
expected = QuantumCircuit(qr2, qr1)
expected.h(qr1[1])
expected.cx(qr1[1], qr1[0])
expected.cx(qr1[0], qr2[0])

self.assertEqual(qc.reverse_bits(), expected)

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)
Comment on lines +1028 to +1046
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.


def test_reverse_bits_with_registers_and_bits(self):
"""Test reversing order of bits with registers and registerless bits."""
qr = QuantumRegister(2, "a")
q = Qubit()
qc = QuantumCircuit(qr, [q])
qc.h(qr[0])
qc.cx(qr[0], qr[1])
qc.cx(qr[1], q)

expected = QuantumCircuit([q], qr)
expected.h(qr[1])
expected.cx(qr[1], qr[0])
expected.cx(qr[0], q)

self.assertEqual(qc.reverse_bits(), expected)

def test_reverse_bits_with_mixed_overlapped_registers(self):
"""Test reversing order of bits with overlapped registers and registerless bits."""
q = Qubit()
qr1 = QuantumRegister(bits=[q, Qubit()], name="qr1")
qr2 = QuantumRegister(bits=[qr1[1], Qubit()], name="qr2")
qc = QuantumCircuit(qr1, qr2, [Qubit()])
qc.h(q)
qc.cx(qr1[0], qr1[1])
qc.cx(qr1[1], qr2[1])
qc.cx(2, 3)

qr2 = QuantumRegister(2, "qr2")
qr1 = QuantumRegister(bits=[qr2[1], q], name="qr1")
expected = QuantumCircuit([Qubit()], qr2, qr1)
expected.h(qr1[1])
expected.cx(qr1[1], qr1[0])
expected.cx(qr1[0], qr2[0])
expected.cx(1, 0)

self.assertEqual(qc.reverse_bits(), expected)

def test_cnot_alias(self):
"""Test that the cnot method alias adds a cx gate."""
qc = QuantumCircuit(2)
Expand Down