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

Fix initial_layout in transpile with loose qubits (backport #10153) #10157

Merged
merged 1 commit into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions qiskit/compiler/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
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
Expand Down Expand Up @@ -762,16 +761,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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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>`__.
31 changes: 25 additions & 6 deletions test/python/compiler/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,14 +627,9 @@ def test_wrong_initial_layout(self):
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")
Expand Down Expand Up @@ -1616,6 +1611,30 @@ def test_transpile_identity_circuit_no_target(self, opt_level):
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):
Expand Down