mirror of https://github.com/Qiskit/qiskit.git
Fix `initial_layout` in `transpile` with loose qubits (#10153)
The previous logic around building a `Layout` object from an `initial_layout` was still couched in the "registers own bits" model, so could not cope with loose bits. It's most convenient to just build the mapping ourselves during the parsing, since the logic is quite specific to the form of the `initial_layout` argument, rather than being something that's inherently tied to the `Layout` class.
This commit is contained in:
parent
558fb0ad21
commit
716b648cb9
|
@ -28,7 +28,6 @@ import warnings
|
|||
from qiskit import user_config
|
||||
from qiskit.circuit.quantumcircuit import QuantumCircuit
|
||||
from qiskit.circuit.quantumregister import Qubit
|
||||
from qiskit.converters import isinstanceint, isinstancelist
|
||||
from qiskit.dagcircuit import DAGCircuit
|
||||
from qiskit.providers.backend import Backend
|
||||
from qiskit.providers.models import BackendProperties
|
||||
|
@ -757,16 +756,27 @@ def _parse_initial_layout(initial_layout, circuits):
|
|||
def _layout_from_raw(initial_layout, circuit):
|
||||
if initial_layout is None or isinstance(initial_layout, Layout):
|
||||
return initial_layout
|
||||
elif isinstancelist(initial_layout):
|
||||
if all(isinstanceint(elem) for elem in initial_layout):
|
||||
initial_layout = Layout.from_intlist(initial_layout, *circuit.qregs)
|
||||
elif all(elem is None or isinstance(elem, Qubit) for elem in initial_layout):
|
||||
initial_layout = Layout.from_qubit_list(initial_layout, *circuit.qregs)
|
||||
elif isinstance(initial_layout, dict):
|
||||
initial_layout = Layout(initial_layout)
|
||||
if isinstance(initial_layout, dict):
|
||||
return Layout(initial_layout)
|
||||
# Should be an iterable either of ints or bits/None.
|
||||
specifier = tuple(initial_layout)
|
||||
if all(phys is None or isinstance(phys, Qubit) for phys in specifier):
|
||||
mapping = {phys: virt for phys, virt in enumerate(specifier) if virt is not None}
|
||||
if len(mapping) != circuit.num_qubits:
|
||||
raise TranspilerError(
|
||||
f"'initial_layout' ({len(mapping)}) and circuit ({circuit.num_qubits}) had"
|
||||
" different numbers of qubits"
|
||||
)
|
||||
else:
|
||||
raise TranspilerError("The initial_layout parameter could not be parsed")
|
||||
return initial_layout
|
||||
if len(specifier) != circuit.num_qubits:
|
||||
raise TranspilerError(
|
||||
f"'initial_layout' ({len(specifier)}) and circuit ({circuit.num_qubits}) had"
|
||||
" different numbers of qubits"
|
||||
)
|
||||
if len(specifier) != len(set(specifier)):
|
||||
raise TranspilerError(f"'initial_layout' contained duplicate entries: {specifier}")
|
||||
mapping = {int(phys): virt for phys, virt in zip(specifier, circuit.qubits)}
|
||||
return Layout(mapping)
|
||||
|
||||
# multiple layouts?
|
||||
if isinstance(initial_layout, list) and any(
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
fixes:
|
||||
- |
|
||||
Using ``initial_layout`` in calls to :func:`.transpile` will no longer error if the
|
||||
circuit contains qubits not in any registers, or qubits that exist in more than one
|
||||
register. See `#10125 <https://github.com/Qiskit/qiskit-terra/issues/10125>`__.
|
|
@ -627,14 +627,9 @@ class TestTranspile(QiskitTestCase):
|
|||
QuantumRegister(3, "q")[2],
|
||||
]
|
||||
|
||||
with self.assertRaises(TranspilerError) as cm:
|
||||
with self.assertRaisesRegex(TranspilerError, "different numbers of qubits"):
|
||||
transpile(qc, backend, initial_layout=bad_initial_layout)
|
||||
|
||||
self.assertEqual(
|
||||
"FullAncillaAllocation: The layout refers to a qubit that does not exist in circuit.",
|
||||
cm.exception.message,
|
||||
)
|
||||
|
||||
def test_parameterized_circuit_for_simulator(self):
|
||||
"""Verify that a parameterized circuit can be transpiled for a simulator backend."""
|
||||
qr = QuantumRegister(2, name="qr")
|
||||
|
@ -1616,6 +1611,30 @@ class TestTranspile(QiskitTestCase):
|
|||
result = transpile(qc, optimization_level=opt_level)
|
||||
self.assertEqual(empty_qc, result)
|
||||
|
||||
@data(0, 1, 2, 3)
|
||||
def test_initial_layout_with_loose_qubits(self, opt_level):
|
||||
"""Regression test of gh-10125."""
|
||||
qc = QuantumCircuit([Qubit(), Qubit()])
|
||||
qc.cx(0, 1)
|
||||
transpiled = transpile(qc, initial_layout=[1, 0], optimization_level=opt_level)
|
||||
self.assertIsNotNone(transpiled.layout)
|
||||
self.assertEqual(
|
||||
transpiled.layout.initial_layout, Layout({0: qc.qubits[1], 1: qc.qubits[0]})
|
||||
)
|
||||
|
||||
@data(0, 1, 2, 3)
|
||||
def test_initial_layout_with_overlapping_qubits(self, opt_level):
|
||||
"""Regression test of gh-10125."""
|
||||
qr1 = QuantumRegister(2, "qr1")
|
||||
qr2 = QuantumRegister(bits=qr1[:])
|
||||
qc = QuantumCircuit(qr1, qr2)
|
||||
qc.cx(0, 1)
|
||||
transpiled = transpile(qc, initial_layout=[1, 0], optimization_level=opt_level)
|
||||
self.assertIsNotNone(transpiled.layout)
|
||||
self.assertEqual(
|
||||
transpiled.layout.initial_layout, Layout({0: qc.qubits[1], 1: qc.qubits[0]})
|
||||
)
|
||||
|
||||
|
||||
@ddt
|
||||
class TestPostTranspileIntegration(QiskitTestCase):
|
||||
|
|
Loading…
Reference in New Issue