Skip to content

Commit 5ebe4f9

Browse files
Refactor: extract _compile() from to_braket() (#288)
* Refactor: extract _compile() from to_braket() Extract compilation logic into a new private _compile() function that returns compiled Qiskit circuits (along with relevant metadata) without translating to Braket. - Add _compile() internal function that returns a _CompilationContext dataclass with compiled circuits and resolved compilation state - Simplify to_braket() to call _compile() then translate * Add tests for qiskit_compile function * Add OpenQASM 3 test for qiskit_compile and fix import sorting * PR comments resolved, and function made private * removed unnecessary empty lines * address PR comments * linter fixes --------- Co-authored-by: Cody Wang <speller26@gmail.com>
1 parent 9ad6c10 commit 5ebe4f9

File tree

1 file changed

+140
-74
lines changed

1 file changed

+140
-74
lines changed

qiskit_braket_provider/providers/adapter.py

Lines changed: 140 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import warnings
1010
from collections import defaultdict
1111
from collections.abc import Callable, Iterable, Mapping, Sequence
12+
from dataclasses import dataclass
1213
from math import inf, pi, prod
1314
from numbers import Number
1415
from typing import TypeVar
@@ -982,7 +983,21 @@ def _restore_verbatim_boxes(
982983
return reconstructed_circuit
983984

984985

985-
def to_braket(
986+
@dataclass(frozen=True)
987+
class _CompilationContext:
988+
"""Internal result from _compile containing compiled circuits and resolved state."""
989+
990+
circuits: list[QuantumCircuit]
991+
single_instance: bool
992+
target: Target | None
993+
qubit_labels: Sequence[int] | None
994+
verbatim: bool | None
995+
basis_gates: Sequence[str] | None
996+
angle_restrictions: Mapping[str, Mapping[int, set[float] | tuple[float, float]]] | None
997+
pass_manager: PassManager | None
998+
999+
1000+
def _compile(
9861001
circuits: _Translatable | Iterable[_Translatable] = None,
9871002
*args,
9881003
qubit_labels: Sequence[int] | None = None,
@@ -1002,77 +1017,9 @@ def to_braket(
10021017
verbatim_box_name: str = _BRAKET_VERBATIM_BOX_NAME,
10031018
layout_method: str | None = None,
10041019
routing_method: str | None = None,
1005-
) -> Circuit | list[Circuit]:
1006-
"""Converts a single or list of Qiskit QuantumCircuits to a single or list of Braket Circuits.
1007-
1008-
The recommended way to use this method is to minimally pass in qubit labels and a target
1009-
(instead of basis gates and coupling map). This ensures that the translated circuit is actually
1010-
supported by the device (and doesn't, for example, include unsupported parameters for gates).
1011-
The latter guarantees that the output Braket circuit uses the qubit labels of the Braket device,
1012-
which are not necessarily contiguous.
1020+
seed_transpiler: int | None = None,
1021+
) -> _CompilationContext:
10131022

1014-
Args:
1015-
circuits (QuantumCircuit | Circuit | Program | str | Iterable): Qiskit or Braket
1016-
circuit(s) or OpenQASM 3 program(s) to transpile and translate to Braket.
1017-
qubit_labels (Sequence[int] | None): A list of (not necessarily contiguous) indices of
1018-
qubits in the underlying Amazon Braket device. If not supplied, then the indices are
1019-
assumed to be contiguous. Default: ``None``.
1020-
target (Target | None): A backend transpiler target. Can only be provided
1021-
if basis_gates is ``None``. Default: ``None``.
1022-
verbatim (bool): Whether to translate the circuit without any modification, in other
1023-
words without transpiling it. Default: ``False``.
1024-
basis_gates (Sequence[str] | None): The gateset to transpile to. Can only be provided
1025-
if target is ``None``. If ``None`` and target is ``None``, the transpiler will use
1026-
all gates defined in the Braket SDK. Default: ``None``.
1027-
coupling_map (list[list[int]] | None): If provided, will transpile to a circuit
1028-
with this coupling map. Default: ``None``.
1029-
angle_restrictions (Mapping[str, Mapping[int, set[float] | tuple[float, float]]] | None):
1030-
Mapping of gate names to parameter angle constraints used to
1031-
validate numeric parameters. Default: ``None``.
1032-
optimization_level (int | None): The optimization level to pass to ``qiskit.transpile``.
1033-
From Qiskit:
1034-
1035-
* 0: no optimization - basic translation, no optimization, trivial layout
1036-
* 1: light optimization - routing + potential SaberSwap, some gate cancellation
1037-
and 1Q gate folding
1038-
* 2: medium optimization - better routing (noise aware) and commutative cancellation
1039-
* 3: high optimization - gate resynthesis and unitary-breaking passes
1040-
1041-
Default: 0.
1042-
callback (Callable | None): A callback function that will be called after each transpiler
1043-
pass execution. Default: ``None``.
1044-
num_processes (int | None): The maximum number of parallel transpilation processes for
1045-
multiple circuits. Default: ``None``.
1046-
pass_manager (PassManager): `PassManager` to transpile the circuit; will raise an error if
1047-
used in conjunction with a target, basis gates, or connectivity. Default: ``None``.
1048-
braket_device (Device): Braket device to transpile to. Can only be provided if `target`
1049-
and ``basis_gates`` are ``None``. Default: ``None``.
1050-
add_measurements (bool): Whether to add measurements when translating Braket circuits.
1051-
Default: True.
1052-
circuit (QuantumCircuit | Circuit | Program | str | Iterable | None): Qiskit or Braket
1053-
circuit(s) or OpenQASM 3 program(s) to transpile and translate to Braket.
1054-
Default: ``None``. DEPRECATED: use first positional argument or ``circuits`` instead.
1055-
connectivity (list[list[int]] | None): If provided, will transpile to a circuit
1056-
with this connectivity. Default: ``None``. DEPRECATED: use ``coupling_map`` instead.
1057-
verbatim_box_name (str): The label name used to identify verbatim BoxOp operations
1058-
in Qiskit circuits. When circuits contain BoxOp operations with this label, they
1059-
will be preserved during transpilation by temporarily replacing them with barriers.
1060-
Default: ``"verbatim"``.
1061-
layout_method (str | None): The layout method to use during transpilation. If ``None``
1062-
and the circuit contains verbatim boxes, defaults to ``'trivial'`` to preserve
1063-
physical qubit mappings. Otherwise uses Qiskit's default. Default: ``None``.
1064-
routing_method (str | None): The routing method to use during transpilation. If ``None``
1065-
and the circuit contains verbatim boxes, defaults to ``'none'`` to disable routing
1066-
and preserve physical qubit structure. Otherwise uses Qiskit's default. Default: ``None``.
1067-
1068-
Raises:
1069-
ValueError: If more than one of `target`, ``basis_gates``
1070-
or ``coupling_map``/``connectivity``, ``pass_manager``, and ``braket_device``
1071-
are passed together, or if `qubit_labels` is passed with ``braket_device``.
1072-
1073-
Returns:
1074-
Circuit | list[Circuit]: Braket circuit or circuits
1075-
"""
10761023
circuits, single_instance = _get_circuits(circuits, circuit, add_measurements)
10771024
if len(args) > 4:
10781025
raise ValueError(f"Unknown arguments passed: {args[4:]}")
@@ -1166,6 +1113,7 @@ def to_braket(
11661113
num_processes=num_processes,
11671114
layout_method=effective_layout_method,
11681115
routing_method=effective_routing_method,
1116+
seed_transpiler=seed_transpiler,
11691117
)
11701118
if isinstance(target, _SubstitutedTarget):
11711119
circuits = target._substitute(circuits)
@@ -1176,13 +1124,131 @@ def to_braket(
11761124
for circ, verbatim_boxes in zip(circuits, all_verbatim_boxes)
11771125
]
11781126

1127+
return _CompilationContext(
1128+
circuits=circuits,
1129+
single_instance=single_instance,
1130+
target=target,
1131+
qubit_labels=qubit_labels,
1132+
verbatim=verbatim,
1133+
basis_gates=basis_gates,
1134+
angle_restrictions=angle_restrictions,
1135+
pass_manager=pass_manager,
1136+
)
1137+
1138+
1139+
def to_braket(
1140+
circuits: _Translatable | Iterable[_Translatable] = None,
1141+
*args,
1142+
qubit_labels: Sequence[int] | None = None,
1143+
target: Target | None = None,
1144+
verbatim: bool | None = None,
1145+
basis_gates: Sequence[str] | None = None,
1146+
coupling_map: list[list[int]] | None = None,
1147+
angle_restrictions: Mapping[str, Mapping[int, set[float] | tuple[float, float]]] | None = None,
1148+
optimization_level: int = 0,
1149+
callback: Callable | None = None,
1150+
num_processes: int | None = None,
1151+
pass_manager: PassManager | None = None,
1152+
braket_device: Device | None = None,
1153+
add_measurements: bool = True,
1154+
circuit: _Translatable | Iterable[_Translatable] | None = None,
1155+
connectivity: list[list[int]] | None = None,
1156+
verbatim_box_name: str = _BRAKET_VERBATIM_BOX_NAME,
1157+
layout_method: str | None = None,
1158+
routing_method: str | None = None,
1159+
seed_transpiler: int | None = None,
1160+
) -> Circuit | list[Circuit]:
1161+
"""Converts a single or list of Qiskit QuantumCircuits to a single or list of Braket Circuits.
1162+
1163+
The recommended way to use this method is to minimally pass in qubit labels and a target
1164+
(instead of basis gates and coupling map). This ensures that the translated circuit is actually
1165+
supported by the device (and doesn't, for example, include unsupported parameters for gates).
1166+
The latter guarantees that the output Braket circuit uses the qubit labels of the Braket device,
1167+
which are not necessarily contiguous.
1168+
1169+
Args:
1170+
circuits (QuantumCircuit | Circuit | Program | str | Iterable): Qiskit or Braket
1171+
circuit(s) or OpenQASM 3 program(s) to transpile and translate to Braket.
1172+
qubit_labels (Sequence[int] | None): A list of (not necessarily contiguous) indices of
1173+
qubits in the underlying Amazon Braket device. If not supplied, then the indices are
1174+
assumed to be contiguous. Default: ``None``.
1175+
target (Target | None): A backend transpiler target. Can only be provided
1176+
if basis_gates is ``None``. Default: ``None``.
1177+
verbatim (bool): Whether to translate the circuit without any modification, in other
1178+
words without transpiling it. Default: ``False``.
1179+
basis_gates (Sequence[str] | None): The gateset to transpile to. Can only be provided
1180+
if target is ``None``. If ``None`` and target is ``None``, the transpiler will use
1181+
all gates defined in the Braket SDK. Default: ``None``.
1182+
coupling_map (list[list[int]] | None): If provided, will transpile to a circuit
1183+
with this coupling map. Default: ``None``.
1184+
angle_restrictions (Mapping[str, Mapping[int, set[float] | tuple[float, float]]] | None):
1185+
Mapping of gate names to parameter angle constraints used to
1186+
validate numeric parameters. Default: ``None``.
1187+
optimization_level (int | None): The optimization level to pass to ``qiskit.transpile``.
1188+
From Qiskit:
1189+
1190+
* 0: no optimization - basic translation, no optimization, trivial layout
1191+
* 1: light optimization - routing + potential SaberSwap, some gate cancellation
1192+
and 1Q gate folding
1193+
* 2: medium optimization - better routing (noise aware) and commutative cancellation
1194+
* 3: high optimization - gate resynthesis and unitary-breaking passes
1195+
1196+
Default: 0.
1197+
callback (Callable | None): A callback function that will be called after each transpiler
1198+
pass execution. Default: ``None``.
1199+
num_processes (int | None): The maximum number of parallel transpilation processes for
1200+
multiple circuits. Default: ``None``.
1201+
pass_manager (PassManager): `PassManager` to transpile the circuit; will raise an error if
1202+
used in conjunction with a target, basis gates, or connectivity. Default: ``None``.
1203+
braket_device (Device): Braket device to transpile to. Can only be provided if `target`
1204+
and ``basis_gates`` are ``None``. Default: ``None``.
1205+
add_measurements (bool): Whether to add measurements when translating Braket circuits.
1206+
Default: True.
1207+
circuit (QuantumCircuit | Circuit | Program | str | Iterable | None): Qiskit or Braket
1208+
circuit(s) or OpenQASM 3 program(s) to transpile and translate to Braket.
1209+
Default: ``None``. DEPRECATED: use first positional argument or ``circuits`` instead.
1210+
connectivity (list[list[int]] | None): If provided, will transpile to a circuit
1211+
with this connectivity. Default: ``None``. DEPRECATED: use ``coupling_map`` instead.
1212+
verbatim_box_name (str): The label name used to identify verbatim BoxOp operations
1213+
in Qiskit circuits. When circuits contain BoxOp operations with this label, they
1214+
will be preserved during transpilation by temporarily replacing them with barriers.
1215+
Default: ``"verbatim"``.
1216+
layout_method (str | None): The layout method to use during transpilation. If ``None``
1217+
and the circuit contains verbatim boxes, defaults to ``'trivial'`` to preserve
1218+
physical qubit mappings. Otherwise uses Qiskit's default. Default: ``None``.
1219+
routing_method (str | None): The routing method to use during transpilation. If ``None``
1220+
and the circuit contains verbatim boxes, defaults to ``'none'`` to disable routing
1221+
and preserve physical qubit structure. Otherwise uses Qiskit's default. Default: ``None``.
1222+
seed_transpiler (int | None): This specifies a seed used for the stochastic parts
1223+
of the transpiler. Default: ``None``.
1224+
1225+
Raises:
1226+
ValueError: If more than one of `target`, ``basis_gates``
1227+
or ``coupling_map``/``connectivity``, ``pass_manager``, and ``braket_device``
1228+
are passed together, or if `qubit_labels` is passed with ``braket_device``.
1229+
1230+
Returns:
1231+
Circuit | list[Circuit]: Braket circuit or circuits
1232+
"""
1233+
result = _compile(
1234+
circuits, *args,
1235+
qubit_labels=qubit_labels, target=target, verbatim=verbatim,
1236+
basis_gates=basis_gates, coupling_map=coupling_map,
1237+
angle_restrictions=angle_restrictions, optimization_level=optimization_level,
1238+
callback=callback, num_processes=num_processes, pass_manager=pass_manager,
1239+
braket_device=braket_device, add_measurements=add_measurements,
1240+
circuit=circuit, connectivity=connectivity, verbatim_box_name=verbatim_box_name,
1241+
layout_method=layout_method, routing_method=routing_method,
1242+
seed_transpiler=seed_transpiler,
1243+
)
11791244
translated = [
11801245
_translate_to_braket(
1181-
circ, target, qubit_labels, verbatim, basis_gates, angle_restrictions, pass_manager
1246+
circ, result.target, result.qubit_labels, result.verbatim,
1247+
result.basis_gates, result.angle_restrictions, result.pass_manager,
11821248
)
1183-
for circ in circuits
1249+
for circ in result.circuits
11841250
]
1185-
return translated[0] if single_instance else translated
1251+
return translated[0] if result.single_instance else translated
11861252

11871253

11881254
def _get_circuits(

0 commit comments

Comments
 (0)