mirror of https://github.com/Qiskit/qiskit.git
Fix inner qubit mapping in UnitarySynthesis pass. (#10405)
This commit is contained in:
parent
645598a4c0
commit
100a997440
|
@ -19,7 +19,7 @@ from itertools import product
|
|||
from functools import partial
|
||||
import numpy as np
|
||||
|
||||
from qiskit.converters import circuit_to_dag
|
||||
from qiskit.converters import circuit_to_dag, dag_to_circuit
|
||||
from qiskit.transpiler import CouplingMap, Target
|
||||
from qiskit.transpiler.basepasses import TransformationPass
|
||||
from qiskit.transpiler.exceptions import TranspilerError
|
||||
|
@ -41,7 +41,6 @@ from qiskit.circuit.library.standard_gates import (
|
|||
ECRGate,
|
||||
)
|
||||
from qiskit.transpiler.passes.synthesis import plugin
|
||||
from qiskit.transpiler.passes.utils import control_flow
|
||||
from qiskit.transpiler.passes.optimization.optimize_1q_decomposition import (
|
||||
Optimize1qGatesDecomposition,
|
||||
)
|
||||
|
@ -442,29 +441,40 @@ class UnitarySynthesis(TransformationPass):
|
|||
if self.method == "default":
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
plugin_method._approximation_degree = self._approximation_degree
|
||||
return self._run_main_loop(
|
||||
dag, plugin_method, plugin_kwargs, default_method, default_kwargs
|
||||
)
|
||||
|
||||
def _run_main_loop(self, dag, plugin_method, plugin_kwargs, default_method, default_kwargs):
|
||||
"""Inner loop for the optimizer, after all DAG-independent set-up has been completed."""
|
||||
|
||||
def _recurse(dag):
|
||||
# This isn't quite a trivially recursive call because we need to close over the
|
||||
# arguments to the function. The loop is sufficiently long that it's cleaner to do it
|
||||
# in a separate method rather than define a helper closure within `self.run`.
|
||||
return self._run_main_loop(
|
||||
dag, plugin_method, plugin_kwargs, default_method, default_kwargs
|
||||
)
|
||||
|
||||
for node in dag.op_nodes(ControlFlowOp):
|
||||
node.op = control_flow.map_blocks(_recurse, node.op)
|
||||
|
||||
dag_bit_indices = (
|
||||
qubit_indices = (
|
||||
{bit: i for i, bit in enumerate(dag.qubits)}
|
||||
if plugin_method.supports_coupling_map or default_method.supports_coupling_map
|
||||
else {}
|
||||
)
|
||||
return self._run_main_loop(
|
||||
dag, qubit_indices, plugin_method, plugin_kwargs, default_method, default_kwargs
|
||||
)
|
||||
|
||||
def _run_main_loop(
|
||||
self, dag, qubit_indices, plugin_method, plugin_kwargs, default_method, default_kwargs
|
||||
):
|
||||
"""Inner loop for the optimizer, after all DAG-independent set-up has been completed."""
|
||||
for node in dag.op_nodes(ControlFlowOp):
|
||||
node.op = node.op.replace_blocks(
|
||||
[
|
||||
dag_to_circuit(
|
||||
self._run_main_loop(
|
||||
circuit_to_dag(block),
|
||||
{
|
||||
inner: qubit_indices[outer]
|
||||
for inner, outer in zip(block.qubits, node.qargs)
|
||||
},
|
||||
plugin_method,
|
||||
plugin_kwargs,
|
||||
default_method,
|
||||
default_kwargs,
|
||||
),
|
||||
copy_operations=False,
|
||||
)
|
||||
for block in node.op.blocks
|
||||
]
|
||||
)
|
||||
|
||||
for node in dag.named_nodes(*self._synth_gates):
|
||||
if self._min_qubits is not None and len(node.qargs) < self._min_qubits:
|
||||
|
@ -481,7 +491,7 @@ class UnitarySynthesis(TransformationPass):
|
|||
if method.supports_coupling_map:
|
||||
kwargs["coupling_map"] = (
|
||||
self._coupling_map,
|
||||
[dag_bit_indices[x] for x in node.qargs],
|
||||
[qubit_indices[x] for x in node.qargs],
|
||||
)
|
||||
synth_dag = method.run(unitary, **kwargs)
|
||||
if synth_dag is not None:
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixed an issue with :class:`.UnitarySynthesis` when using the ``target``
|
||||
parameter where circuits with control flow were not properly mapped
|
||||
to the target.
|
|
@ -809,6 +809,38 @@ class TestUnitarySynthesis(QiskitTestCase):
|
|||
self.assertEqual(cbody.count_ops().keys(), {"u", "cx"})
|
||||
self.assertEqual(qc_uni1_mat, Operator(cbody))
|
||||
|
||||
def test_mapping_control_flow(self):
|
||||
"""Test that inner dags use proper qubit mapping."""
|
||||
qr = QuantumRegister(3, "q")
|
||||
qc = QuantumCircuit(qr)
|
||||
|
||||
# Create target that supports CX only between 0 and 2.
|
||||
fake_target = Target()
|
||||
fake_target.add_instruction(CXGate(), {(0, 2): None})
|
||||
fake_target.add_instruction(
|
||||
UGate(Parameter("t"), Parameter("p"), Parameter("l")),
|
||||
{
|
||||
(0,): None,
|
||||
(1,): None,
|
||||
(2,): None,
|
||||
},
|
||||
)
|
||||
|
||||
qc_uni1 = QuantumCircuit(2)
|
||||
qc_uni1.swap(0, 1)
|
||||
qc_uni1_mat = Operator(qc_uni1)
|
||||
|
||||
loop_body = QuantumCircuit(2)
|
||||
loop_body.unitary(qc_uni1_mat, [0, 1])
|
||||
|
||||
# Loop body uses qubits 0 and 2, mapped to 0 and 1 in the block.
|
||||
# If synthesis doesn't handle recursive mapping, it'll incorrectly
|
||||
# look for a CX on (0, 1) instead of on (0, 2).
|
||||
qc.for_loop((0,), None, loop_body, [0, 2], [])
|
||||
|
||||
dag = circuit_to_dag(qc)
|
||||
UnitarySynthesis(basis_gates=["u", "cx"], target=fake_target).run(dag)
|
||||
|
||||
def test_single_qubit_with_target(self):
|
||||
"""Test input circuit with only 1q works with target."""
|
||||
qc = QuantumCircuit(1)
|
||||
|
|
Loading…
Reference in New Issue