From c34e0b93dc8a6b3e0e1134616c4a092f61dadb12 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Mon, 17 Oct 2022 19:37:20 +0100 Subject: [PATCH] Fix loose-bit handling in QPY (#8928) A typo in clbit-reconstruction was generating the incorrect number of loose clbits in circuits that contained registers and loose bits, and had more qubits than clbits. A bug elsewhere in the code caused circuits with _no_ registers to be deserialised as containing registers, since the `QuantumCircuit(int, int)` constructor actually does produce two registers (slightly surprisingly, perhaps). --- qiskit/qpy/binary_io/circuits.py | 6 +++--- .../fix-qpy-loose-bits-5283dc4ad3823ce3.yaml | 9 +++++++++ .../circuit/test_circuit_load_from_qpy.py | 20 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml diff --git a/qiskit/qpy/binary_io/circuits.py b/qiskit/qpy/binary_io/circuits.py index 49ca334e4a33..e42e3e346793 100644 --- a/qiskit/qpy/binary_io/circuits.py +++ b/qiskit/qpy/binary_io/circuits.py @@ -950,12 +950,12 @@ def read_circuit(file_obj, version, metadata_deserializer=None): qubits = [Qubit() for _ in range(num_qubits - len(circ.qubits))] circ.add_bits(qubits) if len(circ.clbits) < num_clbits: - clbits = [Clbit() for _ in range(num_qubits - len(circ.clbits))] + clbits = [Clbit() for _ in range(num_clbits - len(circ.clbits))] circ.add_bits(clbits) else: circ = QuantumCircuit( - num_qubits, - num_clbits, + [Qubit() for _ in [None] * num_qubits], + [Clbit() for _ in [None] * num_clbits], name=name, global_phase=global_phase, metadata=metadata, diff --git a/releasenotes/notes/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml b/releasenotes/notes/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml new file mode 100644 index 000000000000..528b7facd6f7 --- /dev/null +++ b/releasenotes/notes/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + QPY deserialisation will no longer add extra :class:`.Clbit` instances to the + circuit if there are both loose :class:`.Clbit`\ s in the circuit and more + :class:`.Qubit`\ s than :class:`.Clbit`\ s. + - | + QPY deserialisation will no longer add registers named `q` and `c` if the + input circuit contained only loose bits. diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py index 9cc5f277580c..5b576f690f24 100644 --- a/test/python/circuit/test_circuit_load_from_qpy.py +++ b/test/python/circuit/test_circuit_load_from_qpy.py @@ -1054,3 +1054,23 @@ def test_standard_control_gates(self): qpy_file.seek(0) new_circuit = load(qpy_file)[0] self.assertEqual(qc, new_circuit) + + def test_load_with_loose_bits(self): + """Test that loading from a circuit with loose bits works.""" + qc = QuantumCircuit([Qubit(), Qubit(), Clbit()]) + qpy_file = io.BytesIO() + dump(qc, qpy_file) + qpy_file.seek(0) + new_circuit = load(qpy_file)[0] + self.assertEqual(tuple(new_circuit.qregs), ()) + self.assertEqual(tuple(new_circuit.cregs), ()) + self.assertEqual(qc, new_circuit) + + def test_load_with_loose_bits_and_registers(self): + """Test that loading from a circuit with loose bits and registers works.""" + qc = QuantumCircuit(QuantumRegister(3), ClassicalRegister(1), [Clbit()]) + qpy_file = io.BytesIO() + dump(qc, qpy_file) + qpy_file.seek(0) + new_circuit = load(qpy_file)[0] + self.assertEqual(qc, new_circuit)