Transpiling circuits and programs to Braket¶
In this notebook, we will learn how to use the Qiskit-Braket provider to transpile arbitrary programs of multiple different formats to QPU-native Braket circuits.
We begin by importing all required classes and functions.
[1]:
from qiskit import QuantumCircuit
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import (
Collect2qBlocks,
ConsolidateBlocks,
UnitarySynthesis,
)
from braket.aws import AwsDevice
from braket.circuits import Circuit
from braket.circuits.serialization import IRType
from braket.devices import Devices
from braket.ir.openqasm import Program
from qiskit_braket_provider import BraketAwsBackend, to_braket
Transpiling Qiskit circuits¶
Let’s begin by looking at a simple Qiskit circuit, a four-qubit GHZ state.
[2]:
def ghz_qiskit(n: int) -> QuantumCircuit:
qc = QuantumCircuit(n, n)
qc.h(0)
for i in range(n - 1):
qc.cx(i, i + 1)
qc.measure_all()
return qc
qc = ghz_qiskit(4)
print(qc)
┌───┐ ░ ┌─┐
q_0: ┤ H ├──■─────────────░─┤M├─────────
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├──■────────░──╫─┤M├──────
└───┘┌─┴─┐ ░ ║ └╥┘┌─┐
q_2: ──────────┤ X ├──■───░──╫──╫─┤M├───
└───┘┌─┴─┐ ░ ║ ║ └╥┘┌─┐
q_3: ───────────────┤ X ├─░──╫──╫──╫─┤M├
└───┘ ░ ║ ║ ║ └╥┘
c: 4/════════════════════════╬══╬══╬══╬═
║ ║ ║ ║
meas: 4/════════════════════════╩══╩══╩══╩═
0 1 2 3
We can call to_braket to convert this circuit to a Braket circuit.
[3]:
print(to_braket(qc))
T : │ 0 │ 1 │ 2 │ 3 │ 4 │
┌───┐ ┌───┐
q0 : ─┤ H ├───●───────────────┤ M ├─
└───┘ │ └───┘
┌─┴─┐ ┌───┐
q1 : ───────┤ X ├───●─────────┤ M ├─
└───┘ │ └───┘
┌─┴─┐ ┌───┐
q2 : ─────────────┤ X ├───●───┤ M ├─
└───┘ │ └───┘
┌─┴─┐ ┌───┐
q3 : ───────────────────┤ X ├─┤ M ├─
└───┘ └───┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │
/Users/caw/Documents/GitHub/qiskit-braket-provider/qiskit_braket_provider/providers/adapter.py:975: UserWarning: The Qiskit circuit contains barrier instructions that are ignored.
warnings.warn("The Qiskit circuit contains barrier instructions that are ignored.")
If we run this circuit on a Braket QPU directly, it’ll be compiled to the device’s native gateset and topology. If we want to do this compilation client-side before sending the circuit to the service, we can pass a few extra parameters to to_braket to get a compatible native circuit.
First, we can pass a backend’s target to get a circuit with native gates and connectivity. Let’s use IQM’s Emerald device.
[4]:
backend = BraketAwsBackend(Devices.IQM.Emerald)
target = backend.target
print(to_braket(qc, target=target))
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌───┐
q0 : ───StartVerbatim───┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├──────────●──────────┤ PRx(1.57, 0.00) ├──────────────────────────────────────────────────●──────────┤ PRx(1.57, 0.00) ├───●───┤ PRx(1.57, 0.00) ├───●───┤ PRx(1.57, 0.00) ├─┤ Z ├─┤ PRx(1.57, 0.00) ├─┤ Z ├─┤ PRx(1.57, 0.00) ├─┤ Z ├───────────────────────────────────────────────────────────────────────────────────────────────EndVerbatim─────────
║ └─────────────────┘ └─────────────────┘ │ └─────────────────┘ │ └─────────────────┘ │ └─────────────────┘ │ └─────────────────┘ └─┬─┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─┬─┘ ║
║ ┌─────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─┴─┐ │ │ │ ║ ┌───┐
q1 : ─────────║─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├────────┤ Z ├────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├─┤ PRx(1.57, 0.00) ├────────┤ Z ├────────┤ PRx(1.57, 0.00) ├─┤ Z ├─┤ PRx(1.57, 0.00) ├─┤ Z ├───────────────────────┼─────────────────────────┼─────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────║────────┤ M ├─
║ └─────────────────┘ └─────────────────┘ └───┘ └─────────────────┘ └─────────────────┘ └─────────────────┘ └───┘ └─────────────────┘ └───┘ └─────────────────┘ └───┘ │ │ │ ║ └───┘
║ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ ║ ┌───┐
q2 : ─────────║─────────┤ PRx(1.57, 0.00) ├────────┤ Z ├────────┤ PRx(1.57, 0.00) ├────────┤ Z ├────────┤ PRx(1.57, 0.00) ├────────┤ Z ├────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───────────────────────────────────────────────────────┼─────────────────────────┼─────────────────────────┼─────────────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├────────║────────┤ M ├─
║ └─────────────────┘ └─┬─┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─────────────────┘ │ │ │ └─┬─┘ └─────────────────┘ └─────────────────┘ ║ └───┘
║ ┌─────────────────┐ │ ┌─────────────────┐ │ ┌─────────────────┐ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ │ ║ ┌───┐
q3 : ─────────║─────────┤ PRx(1.57, 0.00) ├──────────●──────────┤ PRx(1.57, 0.00) ├──────────●──────────┤ PRx(1.57, 0.00) ├──────────●──────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───────────────────────────────────────────────────────┼─────────────────────────┼─────────────────────────┼───┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●──────────────────────────────────────────────────║────────┤ M ├─
║ └─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ └─┬─┘ └─────────────────┘ └─────────────────┘ ║ └───┘
║ ┌─────────────────┐ │ ┌─────────────────┐ │ ┌─────────────────┐ │ │ ║ ┌───┐
q4 : ─────────╨─────────┤ PRx(1.57, 0.00) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────●───┤ PRx(1.57, 0.00) ├───●───┤ PRx(1.57, 0.00) ├───●─────●────────────────────────────────────────────────────────────────────────────────────────────────╨────────┤ M ├─
└─────────────────┘ └─────────────────┘ └─────────────────┘ └───┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │
/Users/caw/Documents/GitHub/qiskit-braket-provider/qiskit_braket_provider/providers/adapter.py:1025: UserWarning: Device does not support global phase; global phase of 1.5707963267948966 will not be included in Braket circuit
warnings.warn(
We see that the circuit now only uses PRx and CZ gates, and is wrapped in a verbatim box to ensure that it isn’t further modified before it runs on the device. However, there’s a problem: the circuit uses qubit 0, does not exist on Emerald! This is because Qiskit expects all qubits to be have contiguous, 0-indexed labels. To get a circuit with the correct qubit labels, we
must also pass in the backend’s qubit_labels property.
[5]:
qubit_labels = backend.qubit_labels
braket_circuit = to_braket(qc, qubit_labels=qubit_labels, target=target)
print(braket_circuit)
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌───┐
q1 : ───StartVerbatim───┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├──────────●──────────┤ PRx(1.57, 0.00) ├──────────────────────────────────────────────────●──────────┤ PRx(1.57, 0.00) ├───●───┤ PRx(1.57, 0.00) ├───●───┤ PRx(1.57, 0.00) ├─┤ Z ├─┤ PRx(1.57, 0.00) ├─┤ Z ├─┤ PRx(1.57, 0.00) ├─┤ Z ├───────────────────────────────────────────────────────────────────────────────────────────────EndVerbatim─────────
║ └─────────────────┘ └─────────────────┘ │ └─────────────────┘ │ └─────────────────┘ │ └─────────────────┘ │ └─────────────────┘ └─┬─┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─┬─┘ ║
║ ┌─────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─┴─┐ │ │ │ ║ ┌───┐
q2 : ─────────║─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├────────┤ Z ├────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├─┤ PRx(1.57, 0.00) ├────────┤ Z ├────────┤ PRx(1.57, 0.00) ├─┤ Z ├─┤ PRx(1.57, 0.00) ├─┤ Z ├───────────────────────┼─────────────────────────┼─────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────║────────┤ M ├─
║ └─────────────────┘ └─────────────────┘ └───┘ └─────────────────┘ └─────────────────┘ └─────────────────┘ └───┘ └─────────────────┘ └───┘ └─────────────────┘ └───┘ │ │ │ ║ └───┘
║ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ ║ ┌───┐
q3 : ─────────║─────────┤ PRx(1.57, 0.00) ├────────┤ Z ├────────┤ PRx(1.57, 0.00) ├────────┤ Z ├────────┤ PRx(1.57, 0.00) ├────────┤ Z ├────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───────────────────────────────────────────────────────┼─────────────────────────┼─────────────────────────┼─────────────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├────────║────────┤ M ├─
║ └─────────────────┘ └─┬─┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─────────────────┘ │ │ │ └─┬─┘ └─────────────────┘ └─────────────────┘ ║ └───┘
║ ┌─────────────────┐ │ ┌─────────────────┐ │ ┌─────────────────┐ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ │ ║ ┌───┐
q4 : ─────────║─────────┤ PRx(1.57, 0.00) ├──────────●──────────┤ PRx(1.57, 0.00) ├──────────●──────────┤ PRx(1.57, 0.00) ├──────────●──────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───────────────────────────────────────────────────────┼─────────────────────────┼─────────────────────────┼───┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●──────────────────────────────────────────────────║────────┤ M ├─
║ └─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ └─┬─┘ └─────────────────┘ └─────────────────┘ ║ └───┘
║ ┌─────────────────┐ │ ┌─────────────────┐ │ ┌─────────────────┐ │ │ ║ ┌───┐
q5 : ─────────╨─────────┤ PRx(1.57, 0.00) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────●───┤ PRx(1.57, 0.00) ├───●───┤ PRx(1.57, 0.00) ├───●─────●────────────────────────────────────────────────────────────────────────────────────────────────╨────────┤ M ├─
└─────────────────┘ └─────────────────┘ └─────────────────┘ └───┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │
Now the circuit’s qubits all exist on Emerald. There’s one more unusual thing about the circuit though, and it’s that it uses more qubits than the original GHZ state. This is because the requested qubits (namely 1, 2, 3, and 4) are not all directly connected, and we need to use ancilla qubits and swaps to ensure 2q gates are applied correctly.
Note: this circuit may have a different number of qubits from the previous one; this is because the Qiskit transpiler is nondeterministic, and may route the circuit differently each time it’s called.
We can confirm that we only measure four qubits:
[6]:
print(braket_circuit._measure_targets)
[Qubit(2), Qubit(5), Qubit(4), Qubit(3)]
In addition to not being connected, the qubits we’ve chosen are not necessarily going to be of the highest quality. If we want to use better qubits, or at least minimize the number of extra qubits, we can pass in an optimization level (default 0) that routes the circuit to higher-quality qubits with the best connectivity.
[7]:
print(to_braket(qc, qubit_labels=qubit_labels, target=target, optimization_level=1))
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │
┌─────────────────┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ ┌───┐
q15 : ───StartVerbatim───┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───────────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●─────────────────────────────────────────────EndVerbatim───┤ M ├─
║ └─────────────────┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─────────────────┘ │ ║ └───┘
║ ┌─────────────────┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ │ │ ║ ┌───┐
q16 : ─────────║─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├─┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●─────────────────────────────────────────────┼──────────────────────────────────────────────────║────────┤ M ├─
║ └─────────────────┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─────────────────┘ │ ║ └───┘
║ ┌─────────────────┐ ┌─────────────────┐ │ │ ║ ┌───┐
q17 : ─────────║─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●───────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────║────────┤ M ├─
║ └─────────────────┘ └─────────────────┘ │ ║ └───┘
║ ┌─────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─────────────────┐ ║ ┌───┐
q23 : ─────────╨─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├─────────────────────────────────────────────────────────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├────────╨────────┤ M ├─
└─────────────────┘ └─────────────────┘ └───┘ └─────────────────┘ └─────────────────┘ └───┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │
/Users/caw/Documents/GitHub/qiskit-braket-provider/qiskit_braket_provider/providers/adapter.py:1025: UserWarning: Device does not support global phase; global phase of 4.71238898038469 will not be included in Braket circuit
warnings.warn(
Here we see that the circuit uses a completely different set of qubits, and only four of them. To summarize the parameters that we’ve passed to to_braket:
targettells the transpiler the device’s constraints, such as allowed gates and their error rates and qubit connectivityqubit_labelsensures that the labelling scheme of the Braket circuit matches that of the deviceoptimization_leveltells the Qiskit transpiler how much effort to spend optimizing the circuit
Transpiling Braket circuits¶
In addition to transpiling Qiskit circuits, we can also transpile Braket circuits. Here, we will define the same GHZ state as we did above, but using a Braket circuit.
[8]:
def ghz(n: int) -> Circuit:
circuit = Circuit().h(0)
for i in range(n - 1):
circuit.cnot(i, i + 1)
return circuit
circuit = ghz(4)
print(circuit)
T : │ 0 │ 1 │ 2 │ 3 │
┌───┐
q0 : ─┤ H ├───●───────────────
└───┘ │
┌─┴─┐
q1 : ───────┤ X ├───●─────────
└───┘ │
┌─┴─┐
q2 : ─────────────┤ X ├───●───
└───┘ │
┌─┴─┐
q3 : ───────────────────┤ X ├─
└───┘
T : │ 0 │ 1 │ 2 │ 3 │
We see that calling to_braket on this circuit with the same target, qubit labels and optimization level results in a circuit identical to the one above:
[9]:
print(to_braket(circuit, qubit_labels=qubit_labels, target=target, optimization_level=1))
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │
┌─────────────────┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ ┌───┐
q15 : ───StartVerbatim───┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───────────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●─────────────────────────────────────────────EndVerbatim───┤ M ├─
║ └─────────────────┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─────────────────┘ │ ║ └───┘
║ ┌─────────────────┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ │ │ ║ ┌───┐
q16 : ─────────║─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├─┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●─────────────────────────────────────────────┼──────────────────────────────────────────────────║────────┤ M ├─
║ └─────────────────┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─────────────────┘ │ ║ └───┘
║ ┌─────────────────┐ ┌─────────────────┐ │ │ ║ ┌───┐
q17 : ─────────║─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●───────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────║────────┤ M ├─
║ └─────────────────┘ └─────────────────┘ │ ║ └───┘
║ ┌─────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─────────────────┐ ║ ┌───┐
q23 : ─────────╨─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├─────────────────────────────────────────────────────────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├────────╨────────┤ M ├─
└─────────────────┘ └─────────────────┘ └───┘ └─────────────────┘ └─────────────────┘ └───┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │
Alternatively, you can pass in a Braket Device object instead of a target and qubits labels:
[10]:
print(to_braket(circuit, braket_device=AwsDevice(Devices.IQM.Emerald), optimization_level=1))
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │
┌─────────────────┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ ┌───┐
q15 : ───StartVerbatim───┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───────────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●─────────────────────────────────────────────EndVerbatim───┤ M ├─
║ └─────────────────┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─────────────────┘ │ ║ └───┘
║ ┌─────────────────┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ │ │ ║ ┌───┐
q16 : ─────────║─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├─┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●─────────────────────────────────────────────┼──────────────────────────────────────────────────║────────┤ M ├─
║ └─────────────────┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─────────────────┘ │ ║ └───┘
║ ┌─────────────────┐ ┌─────────────────┐ │ │ ║ ┌───┐
q17 : ─────────║─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●───────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────║────────┤ M ├─
║ └─────────────────┘ └─────────────────┘ │ ║ └───┘
║ ┌─────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─────────────────┐ ║ ┌───┐
q23 : ─────────╨─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├─────────────────────────────────────────────────────────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├────────╨────────┤ M ├─
└─────────────────┘ └─────────────────┘ └───┘ └─────────────────┘ └─────────────────┘ └───┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │
Transpiling OpenQASM 3¶
In addition to Qiskit and Braket circuits, we can compile any static OpenQASM 3 programs (i.e. non-dynamic circuits). In this first example, we have a program with a custom gate definition, as well as control, inverse and power modifiers.
[11]:
qasm_string = """
qubit[3] q;
gate majority a, b, c {
// set c to the majority of {a, b, c}
ctrl @ x c, b;
ctrl @ x c, a;
ctrl(2) @ x a, b, c;
}
pow(0.5) @ x q[0:1]; // sqrt x
inv @ v q[1]; // inv of (sqrt x)
// this should flip q[2] to 1
majority q[0], q[1], q[2];
"""
print(to_braket(qasm_string, qubit_labels=qubit_labels, target=target, optimization_level=1))
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
┌───────────────────┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ ┌───────────────────┐ ┌──────────────────┐
q26 : ───StartVerbatim───┤ PRx(-1.57, -1.57) ├─┤ PRx(3.14, 0.79) ├───────────────────────────┤ Z ├──┤ PRx(1.57, 1.57) ├──┤ PRx(3.14, 0.00) ├───────────────────────────────────────────────────●────────────────────────────────────────────────────────────────────────────────────────────────●─────────────────────────────────────────────────────────────────────────────────────────────────────────●───┤ PRx(-3.14, -1.96) ├─┤ PRx(3.14, -1.57) ├───●─────────────────────────────────────────────EndVerbatim───
║ └───────────────────┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─────────────────┘ │ │ │ └───────────────────┘ └──────────────────┘ │ ║
║ ┌───────────────────┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ │ ┌─────────────────┐ │ ┌──────────────────┐ ┌──────────────────┐ │ ┌───┐ ┌─────────────────┐ ┌───┐ ┌─────────────────┐ ┌───┐ │ │ ║
q33 : ─────────║─────────┤ PRx(-1.57, -1.57) ├─┤ PRx(3.14, 0.00) ├─┤ Z ├─┤ PRx(1.57, 1.57) ├───┼────┤ PRx(3.14, 0.00) ├────────────────────────●───────────────────────────────────────────────┼───────────────────────────────────────────────●───┤ PRx(-1.57, 2.36) ├──┤ PRx(3.14, -1.18) ├───┼─────────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 0.00) ├─┤ Z ├─┤ PRx(1.57, 0.00) ├─┤ Z ├───┼────────────────────────────────────────────────┼──────────────────────────────────────────────────║────────
║ └───────────────────┘ └─────────────────┘ └─┬─┘ └─────────────────┘ │ └─────────────────┘ │ │ │ └──────────────────┘ └──────────────────┘ │ └─┬─┘ └─────────────────┘ └─┬─┘ └─────────────────┘ └─┬─┘ │ │ ║
║ │ │ ┌───────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌───────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌──────────────────┐ ┌──────────────────┐ ┌─┴─┐ ┌───────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌──────────────────┐ ┌──────────────────┐ │ ┌─────────────────┐ │ ┌─────────────────┐ │ ┌─┴─┐ ┌───────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─────────────────┐ ║
q34 : ─────────╨─────────────────────────────────────────────────────●─────────────────────────●───┤ PRx(-3.14, -3.14) ├─┤ PRx(3.14, 0.00) ├─┤ Z ├─┤ PRx(-2.36, -3.14) ├─┤ PRx(3.14, 0.00) ├─┤ Z ├─┤ PRx(-2.36, 0.00) ├─┤ PRx(3.14, -3.14) ├─┤ Z ├─┤ PRx(-2.36, -3.14) ├─┤ PRx(3.14, 0.00) ├──┤ Z ├─┤ PRx(-0.79, 0.00) ├─┤ PRx(3.14, -3.14) ├───●───┤ PRx(1.57, 0.00) ├───●───┤ PRx(1.57, 0.00) ├───●───┤ Z ├─┤ PRx(-2.36, -3.14) ├─┤ PRx(3.14, 0.00) ├──┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├────────╨────────
└───────────────────┘ └─────────────────┘ └───┘ └───────────────────┘ └─────────────────┘ └───┘ └──────────────────┘ └──────────────────┘ └───┘ └───────────────────┘ └─────────────────┘ └───┘ └──────────────────┘ └──────────────────┘ └─────────────────┘ └─────────────────┘ └───┘ └───────────────────┘ └─────────────────┘ └───┘ └─────────────────┘ └─────────────────┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │
/Users/caw/Documents/GitHub/qiskit-braket-provider/qiskit_braket_provider/providers/adapter.py:1025: UserWarning: Device does not support global phase; global phase of 1.1780972450961826 will not be included in Braket circuit
warnings.warn(
Next we create a program with a function definition, classical arithmetic, input parameters, and a for loop. Since the input string is odd, we should expect the transpiled circuit to consist of a single \(X\) gate, or in Emerald’s native gateset, \(PRx(\pi, 0)\). For a change, we’ll also convert the program to OpenQASM, thus demonstrating an OpenQASM-to-OpenQASM transpilation.
[12]:
qasm_program = Program(
source="""
const int[8] n = 4;
input bit[n] x;
qubit q;
def parity(bit[n] cin) -> bit {
bit c = false;
for int[8] i in [0: n - 1] {
c ^= cin[i];
}
return c;
}
if (parity(x)) {
x q;
} else {
i q;
}
""",
inputs={"x": "1011"},
)
print(
to_braket(qasm_program, qubit_labels=qubit_labels, target=target, optimization_level=1).to_ir(
IRType.OPENQASM
)
)
braketSchemaHeader=BraketSchemaHeader(name='braket.ir.openqasm.program', version='1') source='OPENQASM 3.0;\nbit[1] b;\n#pragma braket verbatim\nbox{\nprx(3.141592653589793, 0.0) $1;\n}\nb[0] = measure $1;' inputs={}
Now, we put it all together by generating the GHZ state directly in OpenQASM, and use the inputs to define a four-qubit observable in binary symplectic form.
[13]:
def ghz_program(n: int, *, x: str, z: str) -> Program:
return Program(
source=f"""
int n = {n};
input bit[n] x;
input bit[n] z;
qubit[n] q;
h q[0];
for int i in [0: n - 2] {{
ctrl @ x q[i], q[i + 1];
}}
def observable(bit[n] x, bit[n] z) {{
for int i in [0: n - 1] {{
if (x[i]) {{
if (z[i]) {{
si q[i];
}}
h q[i];
}}
}}
}}
observable(x, z);
""",
inputs={"x": x, "z": z},
)
ghz_4 = ghz_program(4, x="0110", z="0011")
The observables we passed are \(I_0\), \(X_1\), \(Y_2\), and \(Z_3\), which we can verify from the basis rotations of the translated circuit.
[14]:
print(to_braket(ghz_4))
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │
┌───┐
q0 : ─┤ H ├───●────────────────────────────
└───┘ │
┌─┴─┐ ┌───┐
q1 : ───────┤ X ├───●───┤ H ├──────────────
└───┘ │ └───┘
┌─┴─┐ ┌────┐ ┌───┐
q2 : ─────────────┤ X ├───●───┤ Si ├─┤ H ├─
└───┘ │ └────┘ └───┘
┌─┴─┐
q3 : ───────────────────┤ X ├──────────────
└───┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │
Transpiling to an Emerald circuit:
[15]:
print(to_braket(ghz_4, qubit_labels=qubit_labels, target=target, optimization_level=3))
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │
┌─────────────────┐ ┌─────────────────┐
q41 : ───StartVerbatim───┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────EndVerbatim───
║ └─────────────────┘ └─────────────────┘ │ ║
║ ┌─────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ║
q42 : ─────────║─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├─┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●───┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───────────────────────────────────────────────────────║────────
║ └─────────────────┘ └─────────────────┘ └───┘ └─────────────────┘ └─────────────────┘ │ └─────────────────┘ └─────────────────┘ ║
║ ┌─────────────────┐ ┌─────────────────┐ │ ┌───┐ ┌─────────────────┐ ┌─────────────────┐ ║
q47 : ─────────║─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├─────────────────────────────────────────────────┼───────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 1.57) ├──┤ PRx(3.14, 0.00) ├────────║────────
║ └─────────────────┘ └─────────────────┘ │ └─┬─┘ └─────────────────┘ └─────────────────┘ ║
║ ┌─────────────────┐ ┌─────────────────┐ ┌─┴─┐ ┌─────────────────┐ ┌─────────────────┐ │ ┌──────────────────┐ ┌─────────────────┐ ║
q48 : ─────────╨─────────┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───────────────────────────────────────────────┤ Z ├─┤ PRx(1.57, 1.57) ├─┤ PRx(3.14, 0.00) ├───●───┤ PRx(-1.57, 0.00) ├─┤ PRx(3.14, 0.79) ├────────╨────────
└─────────────────┘ └─────────────────┘ └───┘ └─────────────────┘ └─────────────────┘ └──────────────────┘ └─────────────────┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │
/Users/caw/Documents/GitHub/qiskit-braket-provider/qiskit_braket_provider/providers/adapter.py:1025: UserWarning: Device does not support global phase; global phase of 0.7853981633974492 will not be included in Braket circuit
warnings.warn(
For more examples of the kinds of programs we can transpile, see the amazon-braket-examples notebook on simulating advanced OpenQASM programs.
So far, we’ve been transpiling circuits with Qiskit’s default transpilation steps. However, if we want finer-grained control of the transpiler, we can also use a custom pass manager instead of a device target (of course, the circuit below will not run on Emerald because we’ve used non-native gates).
[16]:
basis_gates = ["rx", "ry", "rxx"]
pass_manager = PassManager(
[
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
UnitarySynthesis(basis_gates),
]
)
print(to_braket(ghz_4, qubit_labels=qubit_labels, pass_manager=pass_manager))
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │
┌───────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
q1 : ───StartVerbatim───┤ Rx(-0.79) ├─┤ Ry(3.14) ├──┤ Rx(1.57) ├────────────────┤ XX(-1.57) ├─┤ Rx(1.57) ├──┤ Rx(0.79) ├─┤ Ry(1.57) ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────EndVerbatim───
║ └───────────┘ └──────────┘ └──────────┘ └─────┬─────┘ └──────────┘ └──────────┘ └──────────┘ ║
║ ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌───────────┐ ┌─────┴─────┐ ┌───────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────┐ ║
q2 : ─────────║─────────┤ Rx(2.36) ├──┤ Ry(3.14) ├──┤ Ry(-3.14) ├─┤ Rx(-1.57) ├─┤ XX(-1.57) ├─┤ Ry(-3.14) ├─┤ Rx(1.57) ├─┤ Rx(0.79) ├─┤ Ry(3.14) ├─┤ Ry(1.57) ├─┤ Rx(1.57) ├─┤ XX(-1.57) ├─┤ Rx(1.57) ├──┤ Rx(-1.57) ├───────────────────────────────────────────────────────────────────────────────────────────────────────║────────
║ └──────────┘ └──────────┘ └───────────┘ └───────────┘ └───────────┘ └───────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └─────┬─────┘ └──────────┘ └───────────┘ ║
║ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌─────┴─────┐ ┌───────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────┐ ║
q3 : ─────────║─────────┤ Rx(-2.36) ├─┤ Ry(-3.14) ├─┤ Rx(-1.57) ├────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ XX(-1.57) ├─┤ Ry(-3.14) ├─┤ Rx(1.57) ├──┤ Rx(-2.36) ├─┤ Ry(1.57) ├─┤ Rx(1.57) ├─┤ XX(-1.57) ├─┤ Rx(1.57) ├──┤ Rx(-3.14) ├─────────────────────║────────
║ └───────────┘ └───────────┘ └───────────┘ └───────────┘ └───────────┘ └──────────┘ └───────────┘ └──────────┘ └──────────┘ └─────┬─────┘ └──────────┘ └───────────┘ ║
║ ┌──────────┐ ┌───────────┐ ┌───────────┐ ┌─────┴─────┐ ┌───────────┐ ┌──────────┐ ┌──────────┐ ║
q4 : ─────────╨─────────┤ Rx(0.55) ├──┤ Ry(-3.14) ├─┤ Rx(-1.57) ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ XX(-1.57) ├─┤ Ry(-3.14) ├─┤ Rx(1.57) ├──┤ Rx(1.02) ├────────╨────────
└──────────┘ └───────────┘ └───────────┘ └───────────┘ └───────────┘ └──────────┘ └──────────┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │
/Users/caw/Documents/GitHub/qiskit-braket-provider/qiskit_braket_provider/providers/adapter.py:1025: UserWarning: Device does not support global phase; global phase of 4.712388980384691 will not be included in Braket circuit
warnings.warn(
As was the case with using a target, the output Braket circuit is wrapped in a verbatim box so no further modifications are made.
Running on a BraketBackend¶
Calling to_braket produces a Braket circuit, which can works with the Braket Device class, but not the Qiskit Backend class. To run a circuit on a BraketAwsBackend with native transpilation, we simply need to call Backend.run with native=True (BraketAwsBackend supplies the target and qubit labels) and optionally an optimization level. Alternatively, we can supply a pass manager instead of the native keyword and optimization level.
Uncomment the cells below to try this out; note that running these circuits will incur AWS charges.
[17]:
# task = backend.run(to_qiskit(ghz_4), shots=1000, native=True, optimization_level=1)
[18]:
# from qiskit.transpiler import generate_preset_pass_manager
# task = backend.run(to_qiskit(ghz_4), shots=1000, pass_manager=generate_preset_pass_manager(3, backend))