Tutorials
qcr:2606.09537.1

Combining PennyLane with Amazon Braket

PennyLane is a leading open-source framework for quantum differentiable programming and quantum machine learning, and this notebook shows how to combine it with Amazon Braket so that PennyLane's automatic differentiation and optimization run on Braket's high-performance simulators and quantum hardware. Through the amazon-braket-pennylane-plugin, Braket devices appear as standard PennyLane devices, letting you write quantum nodes (QNodes) in PennyLane's familiar interface while executing them on Braket's on-demand simulators or QPUs. The notebook introduces the integration end to end: installing and loading the Braket PennyLane device, constructing a parameterized quantum circuit as a QNode, computing its output and, crucially, its gradients with respect to the circuit parameters using PennyLane's autodifferentiation, and using those gradients to optimize the circuit. It highlights how this pairing brings together PennyLane's rich machine-learning and optimization tooling with Braket's managed, scalable execution, including the ability to evaluate gradients in parallel across Braket's cloud simulators. It is the starting point for the PennyLane-on-Braket examples and a practical introduction to differentiable quantum programming, linking PennyLane and the Amazon Braket SDK into one workflow.
QML
Qubit
Circuit-based
Uploaded 1 day ago
18
Views
GitHub582
Citing this entry? Use this QCR ID
Uploaded by
QL
QCR Librarian

Overview

amazon-braket/amazon-braket-examples
582261
In [ ]:
# --- Setup cell added by QCR (not part of the original tutorial) ---
# Source: amazon-braket/amazon-braket-examples @ 0c0818f315479aab9deebed7e7ed7533ac581923, Apache License 2.0.
# Installs the example's dependencies. If a later cell still reports a missing
# package, restart the runtime/kernel and run again from the top.
%pip install -q amazon-braket-sdk==1.117.3 pennylane==0.45.0 amazon-braket-pennylane-plugin==1.34.1 matplotlib

Combining PennyLane with Amazon Braket

What is PennyLane? PennyLane is a Python library for differentiable programming of quantum computers, allowing you to train a quantum computer the same way as a neural network.

PennyLane integrates with Amazon Braket to add additional features for quantum machine learning and optimization. This introductory tutorial walks you through how to train a quantum circuit using Amazon Braket simulators and PennyLane's automatic differentiation capabilities.

Setup

PennyLane is already installed on Braket notebook instances. On a local machine, PennyLane can be installed by following these instructions. It can then be imported with:

In [1]:
import pennylane as qml
from pennylane import numpy as np

To use Braket as a backend in PennyLane, we have to create a PennyLane device. Here we will first create a device that uses the local Braket simulator that runs on your local laptop (or on the server that hosts this notebook).

In [2]:
wires = 2  # Number of qubits

dev = qml.device("braket.local.qubit", wires=wires)

Below we will also show you how to scale out simulations to the AWS cloud.

Defining a circuit

We will choose a simple two-qubit circuit with two controllable rotations and a CNOT gate.

In [3]:
@qml.qnode(dev)
def circuit(params):
    qml.RX(params[0], wires=0)
    qml.RY(params[1], wires=1)
    qml.CNOT(wires=[0, 1])
    return qml.expval(qml.PauliZ(1))

The qml.qnode(dev) decorator binds the circuit to the local Braket device. Now, every time that circuit() is called, the quantum computation defined in the function above will be executed with Braket.

Note PennyLane also supports automatic differentiation with PyTorch and TensorFlow interfaces. The choice of interface can be specified using: @qml.qnode(dev, interface="<interface>").

Evaluating the circuit and accessing its gradient

Let's set some values for our controllable parameters:

In [4]:
params = np.array([0.1, 0.2], requires_grad=True)

The circuit can be evaluated with these parameters using

In [5]:
print("Expectation value of circuit:", circuit(params))
Expectation value of circuit: 0.9751703272018161
In [6]:
print("Drawing of circuit:\n")
print(qml.draw(circuit)(params))
Drawing of circuit:

0: ──RX(0.10)─╭●─┤     
1: ──RY(0.20)─╰X─┤  <Z>

A crucial element of machine learning and optimization is accessing the gradient of a model with respect to its parameters. This functionality is built into PennyLane:

In [7]:
dcircuit = qml.grad(circuit)

Here, dcircuit is a callable function that evaluates the gradient of the circuit, i.e., its partial derivatives with respect to the controllable parameters.

In [8]:
dcircuit(params)
array([-0.0978434 , -0.19767681])

Training the circuit

Suppose we now want to minimize the output of the circuit by updating its parameters. This can be done using gradient-based optimization.

First, an optimizer is fixed:

In [9]:
opt = qml.GradientDescentOptimizer(stepsize=0.1)

The next step is to run the optimizer for a chosen number of iterations:

In [10]:
import matplotlib.pyplot as plt

iterations = 20

costs = []

for i in range(iterations):
    params, cost = opt.step_and_cost(circuit, params)
    costs.append(cost)

# Visualize results
costs.append(circuit(params))
plt.plot(costs, "-o")
plt.xlabel("Iterations")
plt.ylabel("Cost")

print("Minimized circuit output:", circuit(params))
print("Optimized parameters:", params)
Minimized circuit output: 0.37261070647126565
Optimized parameters: [0.4839502  1.13630274]
Note The circuit considered here is very simple and can be optimized easily by hand. However, the need for PennyLane's automatic differentiation capabilities becomes apparent as we make the problem more complicated, e.g., with more gates and different types of output measurement. In later demos, we will also see how Braket can be used to parallelize evaluation of the gradient, providing a turbocharger for quantum circuit training in PennyLane.

Running circuits on Braket's on-demand simulator, SV1

So far we have used the local Braket simulator. This is a great choice for quick prototyping, but it is not suitable for large circuits with many qubits and does not provide a connection to quantum hardware.

Amazon Braket also provides access to on-demand, high-performance simulators and quantum processing units (QPUs) from different providers. These devices can be accessed through PennyLane by changing a single line of code, unlocking the potential for machine learning and optimization on quantum hardware and high performance simulators!

In [11]:
# Use Braket SDK Cost Tracking to estimate the cost to run this example
from braket.tracking import Tracker

braket_tasks_cost = Tracker().start()

Each remote Braket device can be selected through its ARN. The supported devices on Braket are listed here. For now, we will pick the on-demand SV1 simulator.

Caution: Running hybrid algorithms on a QPU can take a long time and incur high usage fees charged to your AWS account.

In PennyLane, all remote Braket devices are then accessed through a single PennyLane device named braket.aws.qubit.

In [12]:
from braket.devices import Devices

dev = qml.device("braket.aws.qubit", device_arn=Devices.Amazon.SV1, wires=2)

A follow up tutorial shows you how to use the remote device to run multiple circuits in parallel, while the QAOA tutorial takes a deeper dive into graph optimization, including using SV1 to optimize a 20-node graph.

Let's execute our circuit on SV1, as well as calculating the gradient:

In [13]:
@qml.qnode(dev)
def circuit(params):
    qml.RX(params[0], wires=0)
    qml.RY(params[1], wires=1)
    qml.CNOT(wires=[0, 1])
    return qml.expval(qml.PauliZ(1))


dcircuit = qml.grad(circuit)

print("Result of circuit run on SV1:", circuit(params))
print("Result of gradient calculation on SV1:", dcircuit(params))
Result of circuit run on SV1: 0.37261070647126565
Result of gradient calculation on SV1: [-0.19585986 -0.80291741]
What's next? Check out the other tutorials in this folder to understand how Braket and PennyLane can be combined to solve a range of problems, from graph optimization to quantum chemistry.
In [14]:
print("Quantum Task Summary")
print(braket_tasks_cost.quantum_tasks_statistics())
print(
    "Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2).",
)
print(
    f"Estimated cost to run this example: {braket_tasks_cost.qpu_tasks_cost() + braket_tasks_cost.simulator_tasks_cost():.3f} USD",
)
Quantum Task Summary
{<_Amazon.SV1: 'arn:aws:braket:::device/quantum-simulator/amazon/sv1'>: {'shots': 0, 'tasks': {'COMPLETED': 2}, 'execution_duration': datetime.timedelta(microseconds=6000), 'billed_execution_duration': datetime.timedelta(seconds=6)}}
Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2).
Estimated cost to run this example: 0.008 USD

Running on a QPU with Amazon Braket Hybrid Jobs

In this notebook, the classical part of the algorithm was running locally. For longer-running algorithms or those requiring more intensive compute resources, it's recommended to dispatch the algorithm to Amazon Braket Hybrid Jobs, which fully manages the classical infrastructure, allowing you to focus on the algorithm. For example, you can train a larger circuit or increase the number of iterations.

The second benefit of running the algorithm as a hybrid job is that for iterative algorithms that require repeated calls to a QPU, you retain priority for that QPU. Once your quantum tasks are created in the hybrid job, they run ahead of other tasks waiting in the regular quantum task queue. This is because hybrid jobs have a separate queue from standalone tasks, ensuring that only a single hybrid job can run on a QPU at a time. This means your algorithm will not be interrupted by other quantum tasks, so it will run more efficiently and predictably. However, hybrid jobs have a separate queue from standalone tasks, so only a single hybrid job can run on a QPU at a time. For a single quantum circuit or a batch of circuits, it's recommended to create quantum tasks instead of hybrid jobs. Only iterative algorithms benefit from QPU priority queuing.

Note that hybrid jobs have at least a one-minute startup time since they create a containerized environment on Amazon EC2. So for very short workloads, there is likely no need to create a hybrid job.

You can run your local Python code as an Amazon Braket hybrid job by annotating your code with the `@hybrid_job`` decorator, as shown in the following code example. Only Python 3.12 is supported by default. For custom Python versions, you can choose to use a custom container from Amazon Elastic Container Registry (ECR) (see BYOC).

In the following code, we create a hybrid job for 10 iterations targeting Rigetti Cepheus-1-108Q. Since we specified Cepheus-1-108Q as the device, this job will run once Cepheus-1-108Q is available and has no jobs running ahead of it.

In [ ]:
from braket.jobs import hybrid_job
from braket.jobs.metrics import log_metric

device_arn = Devices.Amazon.SV1
# device_arn = Devices.Rigetti.Cepheus1108Q  # <-- uncomment this line to actually use Cepheus-1-108Q, it will cost about 120 USD


@hybrid_job(device=device_arn, data_format="pickle")  # set priority QPU
def qubit_rotation(stepsize=0.1, iterations=5):
    task_tracker = Tracker().start()  # track Braket quantum tasks costs
    dev = qml.device("braket.aws.qubit", device_arn=device_arn.value, wires=2, shots=1_000)

    params = np.array([0.1, 0.2])

    @qml.qnode(dev)
    def circuit(params):
        qml.RX(params[0], wires=0)
        qml.RY(params[1], wires=1)
        qml.CNOT(wires=[0, 1])
        return qml.expval(qml.PauliZ(1))

    opt = qml.GradientDescentOptimizer(stepsize)

    costs = []
    for i in range(iterations):
        params, cost = opt.step_and_cost(circuit, params)
        costs.append(cost)

        # Record the value of the cost function with each iteration
        log_metric(metric_name="cost_function", value=cost, iteration_number=i)

        # Additionally, keep track of cost in USD for Braket tasks
        braket_task_cost = float(
            task_tracker.qpu_tasks_cost() + task_tracker.simulator_tasks_cost(),
        )
        log_metric(metric_name="braket_cost", value=braket_task_cost, iteration_number=i)

    return {"params": params, "costs": costs, "braket_tasks_cost": braket_task_cost}

How long will it take the hybrid job to run? Let's first check if the device is currently available with AwsDevice(device_arn).is_available().

In [16]:
from braket.aws import AwsDevice

AwsDevice(device_arn).is_available
True

Next, we check the hybrid job queue depth with AwsDevice(device_arn).queue_depth().jobs.

In [17]:
AwsDevice(device_arn).queue_depth().jobs
'0 (1 prioritized hybrid job running)'

If the device is available and there are no hybrid jobs currently running, then it should take about 5 minutes to complete.

Caution: Running the following cell will only run once the QPU is available. This may take a long time and will result in usage fees charged to your AWS account. Only uncomment the cell if you are comfortable with the potential wait-time and costs. We recommend monitoring the Billing & Cost Management Dashboard on the AWS console and being aware that hybrid jobs involving a large number of qubits can be costly.
In [18]:
job = qubit_rotation(stepsize=0.2, iterations=20)
print(job)
AwsQuantumJob('arn':'arn:aws:braket:us-west-1:606892779558:job/d6234c47-fad4-4cda-bf9a-95b1ed20857b')

Once the hybrid job has completed, we can retrieve the results with job.results().

In [19]:
%%time
results = job.result(allow_pickle=True)
print(results)
{'params': tensor([0.7454, 1.9814], requires_grad=True), 'costs': [array(0.928), array(0.904), array(0.9), array(0.91), array(0.886), array(0.852), array(0.812), array(0.778), array(0.744), array(0.664), array(0.596), array(0.486), array(0.35), array(0.258), array(0.278), array(0.104), array(0.062), array(-0.056), array(-0.096), array(-0.212)], 'braket_tasks_cost': 120.0}
CPU times: user 675 ms, sys: 51.8 ms, total: 727 ms
Wall time: 14min 6s
In [20]:
plt.plot(results["costs"], "o-")
plt.xlabel("Iterations")
plt.ylabel("Cost")

print("Minimized circuit output:", circuit(params))
print("Optimized parameters:", params)
Minimized circuit output: 0.37261070647126565
Optimized parameters: [0.4839502  1.13630274]
In [21]:
job_cost = job.result(allow_pickle=True)["braket_tasks_cost"]
sv1_cost = float(braket_tasks_cost.simulator_tasks_cost())

print(
    "Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2).",
)
print(f"Estimated cost to run this example: {job_cost + sv1_cost:.3f} USD")
Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2).
Estimated cost to run this example: 120.011 USD

Join the Discussion

Comments (0)

No comments yet. Be the first to share your thoughts!

Indexed by QCR Librarian

This entry was created automatically from publicly available records. QCR links to public sources and only stores repository content where the license permits redistribution.

Versions

v1 Latest
Jun 17, 2026
qcr:2606.09537.1

Cite all versions? Use the base QCR ID to always reference the latest version of this entry.

Tools used

Amazon Braket SDK

Keywords

pennylane
differentiable-programming
braket
autodiff
qml

You may also like5