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

Use BasisTranslator for unroll 3q or more #10776

Merged
merged 1 commit into from
Sep 6, 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
16 changes: 11 additions & 5 deletions qiskit/transpiler/passes/basis/basis_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class BasisTranslator(TransformationPass):
:ref:`custom_basis_gates` for details on adding custom equivalence rules.
"""

def __init__(self, equivalence_library, target_basis, target=None):
def __init__(self, equivalence_library, target_basis, target=None, min_qubits=0):
"""Initialize a BasisTranslator instance.

Args:
Expand All @@ -104,6 +104,8 @@ def __init__(self, equivalence_library, target_basis, target=None):
this library will not be unrolled by this pass.)
target_basis (list[str]): Target basis names to unroll to, e.g. `['u3', 'cx']`.
target (Target): The backend compilation target
min_qubits (int): The minimum number of qubits for operations in the input
dag to translate.
"""

super().__init__()
Expand All @@ -112,6 +114,7 @@ def __init__(self, equivalence_library, target_basis, target=None):
self._target = target
self._non_global_operations = None
self._qargs_with_non_global_operation = {}
self._min_qubits = min_qubits
if target is not None:
self._non_global_operations = self._target.get_non_global_operation_names()
self._qargs_with_non_global_operation = defaultdict(set)
Expand Down Expand Up @@ -237,7 +240,7 @@ def apply_translation(dag, wire_map):
for node in dag.op_nodes():
node_qargs = tuple(wire_map[bit] for bit in node.qargs)
qubit_set = frozenset(node_qargs)
if node.name in target_basis:
if node.name in target_basis or len(node.qargs) < self._min_qubits:
if isinstance(node.op, ControlFlowOp):
flow_blocks = []
for block in node.op.blocks:
Expand Down Expand Up @@ -327,7 +330,7 @@ def _extract_basis(self, circuit):
@_extract_basis.register
def _(self, dag: DAGCircuit):
for node in dag.op_nodes():
if not dag.has_calibration_for(node):
if not dag.has_calibration_for(node) and len(node.qargs) >= self._min_qubits:
yield (node.name, node.op.num_qubits)
if isinstance(node.op, ControlFlowOp):
for block in node.op.blocks:
Expand All @@ -337,7 +340,10 @@ def _(self, dag: DAGCircuit):
def _(self, circ: QuantumCircuit):
for instruction in circ.data:
operation = instruction.operation
if not circ.has_calibration_for(instruction):
if (
not circ.has_calibration_for(instruction)
and len(instruction.qubits) >= self._min_qubits
):
yield (operation.name, operation.num_qubits)
if isinstance(operation, ControlFlowOp):
for block in operation.blocks:
Expand All @@ -352,7 +358,7 @@ def _extract_basis_target(
qargs_local_source_basis = defaultdict(set)
for node in dag.op_nodes():
qargs = tuple(qarg_indices[bit] for bit in node.qargs)
if dag.has_calibration_for(node):
if dag.has_calibration_for(node) or len(node.qargs) < self._min_qubits:
continue
# Treat the instruction as on an incomplete basis if the qargs are in the
# qargs_with_non_global_operation dictionary or if any of the qubits in qargs
Expand Down
7 changes: 5 additions & 2 deletions qiskit/transpiler/passes/basis/unroll_custom_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
class UnrollCustomDefinitions(TransformationPass):
"""Unrolls instructions with custom definitions."""

def __init__(self, equivalence_library, basis_gates=None, target=None):
def __init__(self, equivalence_library, basis_gates=None, target=None, min_qubits=0):
"""Unrolls instructions with custom definitions.

Args:
Expand All @@ -33,12 +33,15 @@ def __init__(self, equivalence_library, basis_gates=None, target=None):
Ignored if ``target`` is also specified.
target (Optional[Target]): The :class:`~.Target` object corresponding to the compilation
target. When specified, any argument specified for ``basis_gates`` is ignored.
min_qubits (int): The minimum number of qubits for operations in the input
dag to translate.
"""

super().__init__()
self._equiv_lib = equivalence_library
self._basis_gates = basis_gates
self._target = target
self._min_qubits = min_qubits

def run(self, dag):
"""Run the UnrollCustomDefinitions pass on `dag`.
Expand Down Expand Up @@ -69,7 +72,7 @@ def run(self, dag):
if getattr(node.op, "_directive", False):
continue

if dag.has_calibration_for(node):
if dag.has_calibration_for(node) or len(node.qargs) < self._min_qubits:
continue

controlled_gate_open_ctrl = isinstance(node.op, ControlledGate) and node.op._open_ctrl
Expand Down
10 changes: 9 additions & 1 deletion qiskit/transpiler/preset_passmanagers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,15 @@ def generate_unroll_3q(
hls_config=hls_config, coupling_map=None, target=target, use_qubit_indices=False
)
)
unroll_3q.append(Unroll3qOrMore(target=target, basis_gates=basis_gates))
# If there are no target instructions revert to using unroll3qormore so
# routing works.
if basis_gates is None and target is None:
unroll_3q.append(Unroll3qOrMore(target, basis_gates))
else:
unroll_3q.append(
UnrollCustomDefinitions(sel, basis_gates=basis_gates, target=target, min_qubits=3)
)
unroll_3q.append(BasisTranslator(sel, basis_gates, target=target, min_qubits=3))
return unroll_3q


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
features:
- |
Added a new keyword argument, ``min_qubits``, to the constructor of the
:class:`.BasisTranslator` transpiler pass. When set to a non-zero value this
is used to set a minimum number of qubits to filter operations to translate
in the circuit. For example, if ``min_qubits=3`` is set the
:class:`.BasisTranslator` instance will only translate gates in the circuit
that operate on 3 or more qubits.
- |
Added a new keyword argument, ``min_qubits``, to the constructor of the
:class:`.UnrollCustomDefinitions` transpiler pass. When set to a non-zero
value this is used to set a minimum number of qubits to filter operations
to translate in the circuit. For example, if ``min_qubits=3`` is set the
:class:`.UnrollCustomDefinitions` instance will only translate gates in the
circuit that operate on 3 or more qubits.

31 changes: 31 additions & 0 deletions test/python/transpiler/test_basis_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,37 @@ def test_unroll_toffoli(self):
for node in op_nodes:
self.assertIn(node.name, ["h", "t", "tdg", "cx"])

def test_basic_unroll_min_qubits(self):
"""Test decompose a single H into u2."""
qr = QuantumRegister(1, "qr")
circuit = QuantumCircuit(qr)
circuit.h(qr[0])
dag = circuit_to_dag(circuit)
pass_ = UnrollCustomDefinitions(std_eqlib, ["u2"], min_qubits=3)
dag = pass_.run(dag)
pass_ = BasisTranslator(std_eqlib, ["u2"], min_qubits=3)
unrolled_dag = pass_.run(dag)
op_nodes = unrolled_dag.op_nodes()
self.assertEqual(len(op_nodes), 1)
self.assertEqual(op_nodes[0].name, "h")

def test_unroll_toffoli_min_qubits(self):
"""Test unroll toffoli on multi regs to h, t, tdg, cx."""
qr1 = QuantumRegister(2, "qr1")
qr2 = QuantumRegister(1, "qr2")
circuit = QuantumCircuit(qr1, qr2)
circuit.ccx(qr1[0], qr1[1], qr2[0])
circuit.sx(qr1[0])
dag = circuit_to_dag(circuit)
pass_ = UnrollCustomDefinitions(std_eqlib, ["h", "t", "tdg", "cx"], min_qubits=3)
dag = pass_.run(dag)
pass_ = BasisTranslator(std_eqlib, ["h", "t", "tdg", "cx"], min_qubits=3)
unrolled_dag = pass_.run(dag)
op_nodes = unrolled_dag.op_nodes()
self.assertEqual(len(op_nodes), 16)
for node in op_nodes:
self.assertIn(node.name, ["h", "t", "tdg", "cx", "sx"])

def test_unroll_1q_chain_conditional(self):
"""Test unroll chain of 1-qubit gates interrupted by conditional."""

Expand Down
1 change: 0 additions & 1 deletion test/python/transpiler/test_preset_passmanagers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,6 @@ def callback(pass_, **_):
self.assertIn("Unroller", calls)
self.assertNotIn("DenseLayout", calls)
self.assertNotIn("SabreLayout", calls)
self.assertNotIn("BasisTranslator", calls)

@data(0, 1, 2, 3)
def test_invalid_methods_raise_on_control_flow(self, optimization_level):
Expand Down