Fix circuit instruction conversion with loose bits (#7365)

Previously, `circuit_to_instruction` and `circuit_to_gate` were finding
the number of bits in a circuit by summing the sizes of the registers.
This failed if any bits were not in a register, or in more than one
register.  Instead, we can simply ask the input circuit how many bits it
contains.
This commit is contained in:
Jake Lishman 2021-12-07 17:30:21 +00:00 committed by GitHub
parent 2af1016f7b
commit 29c62c4bf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 96 additions and 7 deletions

View File

@ -18,7 +18,7 @@ from qiskit.exceptions import QiskitError
def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None, label=None):
"""Build a ``Gate`` object from a ``QuantumCircuit``.
"""Build a :class:`.Gate` object from a :class:`.QuantumCircuit`.
The gate is anonymous (not tied to a named quantum register),
and so can be inserted into another circuit. The gate will
@ -73,7 +73,7 @@ def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None, label
gate = Gate(
name=circuit.name,
num_qubits=sum(qreg.size for qreg in circuit.qregs),
num_qubits=circuit.num_qubits,
params=[*parameter_dict.values()],
label=label,
)

View File

@ -19,7 +19,7 @@ from qiskit.circuit.classicalregister import ClassicalRegister, Clbit
def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None, label=None):
"""Build an ``Instruction`` object from a ``QuantumCircuit``.
"""Build an :class:`~.circuit.Instruction` object from a :class:`.QuantumCircuit`.
The instruction is anonymous (not tied to a named quantum register),
and so can be inserted into another circuit. The instruction will
@ -48,7 +48,6 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.converters import circuit_to_instruction
%matplotlib inline
q = QuantumRegister(3, 'q')
c = ClassicalRegister(3, 'c')
@ -77,8 +76,8 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None
instruction = Instruction(
name=circuit.name,
num_qubits=sum(qreg.size for qreg in circuit.qregs),
num_clbits=sum(creg.size for creg in circuit.cregs),
num_qubits=circuit.num_qubits,
num_clbits=circuit.num_clbits,
params=[*parameter_dict.values()],
label=label,
)

View File

@ -0,0 +1,8 @@
---
fixes:
- |
Fixed an error in the circuit conversion functions
:func:`.circuit_to_gate` and :func:`.circuit_to_instruction` (and their
associated circuit methods :meth:`.QuantumCircuit.to_gate` and
:meth:`.QuantumCircuit.to_instruction`) when acting on a circuit with
registerless bits, or bits in more than one register.

View File

@ -13,7 +13,7 @@
"""Tests for the converters."""
from qiskit import QuantumRegister, QuantumCircuit
from qiskit.circuit import Gate
from qiskit.circuit import Gate, Qubit
from qiskit.test import QiskitTestCase
from qiskit.exceptions import QiskitError
@ -35,6 +35,39 @@ class TestCircuitToGate(QiskitTestCase):
self.assertIsInstance(gate, Gate)
self.assertEqual(gate.definition[0][1], [q[1], q[6]])
def test_circuit_with_registerless_bits(self):
"""Test a circuit with registerless bits can be converted to a gate."""
qr1 = QuantumRegister(2)
qubits = [Qubit(), Qubit(), Qubit()]
qr2 = QuantumRegister(3)
circ = QuantumCircuit(qr1, qubits, qr2)
circ.cx(3, 5)
gate = circ.to_gate()
self.assertIsInstance(gate, Gate)
self.assertEqual(gate.num_qubits, len(qr1) + len(qubits) + len(qr2))
gate_definition = gate.definition
_, cx_qargs, cx_cargs = gate_definition.data[0]
self.assertEqual(cx_qargs, [gate_definition.qubits[3], gate_definition.qubits[5]])
self.assertEqual(cx_cargs, [])
def test_circuit_with_overlapping_registers(self):
"""Test that the conversion works when the given circuit has bits that are contained in more
than one register."""
qubits = [Qubit() for _ in [None] * 10]
qr1 = QuantumRegister(bits=qubits[:6])
qr2 = QuantumRegister(bits=qubits[4:])
circ = QuantumCircuit(qubits, qr1, qr2)
circ.cx(3, 5)
gate = circ.to_gate()
self.assertIsInstance(gate, Gate)
self.assertEqual(gate.num_qubits, len(qubits))
gate_definition = gate.definition
_, cx_qargs, cx_cargs = gate_definition.data[0]
self.assertEqual(cx_qargs, [gate_definition.qubits[3], gate_definition.qubits[5]])
self.assertEqual(cx_cargs, [])
def test_raises(self):
"""test circuit which can't be converted raises"""
circ1 = QuantumCircuit(3)

View File

@ -16,6 +16,7 @@ import unittest
from qiskit.converters import circuit_to_instruction
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.circuit import Qubit, Clbit
from qiskit.circuit import Parameter
from qiskit.test import QiskitTestCase
from qiskit.exceptions import QiskitError
@ -67,6 +68,54 @@ class TestCircuitToInstruction(QiskitTestCase):
self.assertEqual(inst.definition[1][0].condition, (c[3], False))
self.assertEqual(inst.definition[2][0].condition, (c[5], True))
def test_flatten_circuit_registerless(self):
"""Test that the conversion works when the given circuit has bits that are not contained in
any register."""
qr1 = QuantumRegister(2)
qubits = [Qubit(), Qubit(), Qubit()]
qr2 = QuantumRegister(3)
cr1 = ClassicalRegister(2)
clbits = [Clbit(), Clbit(), Clbit()]
cr2 = ClassicalRegister(3)
circ = QuantumCircuit(qr1, qubits, qr2, cr1, clbits, cr2)
circ.cx(3, 5)
circ.measure(4, 4)
inst = circuit_to_instruction(circ)
self.assertEqual(inst.num_qubits, len(qr1) + len(qubits) + len(qr2))
self.assertEqual(inst.num_clbits, len(cr1) + len(clbits) + len(cr2))
inst_definition = inst.definition
_, cx_qargs, cx_cargs = inst_definition.data[0]
_, measure_qargs, measure_cargs = inst_definition.data[1]
self.assertEqual(cx_qargs, [inst_definition.qubits[3], inst_definition.qubits[5]])
self.assertEqual(cx_cargs, [])
self.assertEqual(measure_qargs, [inst_definition.qubits[4]])
self.assertEqual(measure_cargs, [inst_definition.clbits[4]])
def test_flatten_circuit_overlapping_registers(self):
"""Test that the conversion works when the given circuit has bits that are contained in more
than one register."""
qubits = [Qubit() for _ in [None] * 10]
qr1 = QuantumRegister(bits=qubits[:6])
qr2 = QuantumRegister(bits=qubits[4:])
clbits = [Clbit() for _ in [None] * 10]
cr1 = ClassicalRegister(bits=clbits[:6])
cr2 = ClassicalRegister(bits=clbits[4:])
circ = QuantumCircuit(qubits, clbits, qr1, qr2, cr1, cr2)
circ.cx(3, 5)
circ.measure(4, 4)
inst = circuit_to_instruction(circ)
self.assertEqual(inst.num_qubits, len(qubits))
self.assertEqual(inst.num_clbits, len(clbits))
inst_definition = inst.definition
_, cx_qargs, cx_cargs = inst_definition.data[0]
_, measure_qargs, measure_cargs = inst_definition.data[1]
self.assertEqual(cx_qargs, [inst_definition.qubits[3], inst_definition.qubits[5]])
self.assertEqual(cx_cargs, [])
self.assertEqual(measure_qargs, [inst_definition.qubits[4]])
self.assertEqual(measure_cargs, [inst_definition.clbits[4]])
def test_flatten_parameters(self):
"""Verify parameters from circuit are moved to instruction.params"""
qr = QuantumRegister(3, "qr")