Amazon Braket Beginner Free 7/11 in series 18 min read

Getting Started with Amazon Braket

Set up Amazon Braket, run circuits on the local simulator, and learn how to submit jobs to real quantum hardware on AWS.

What you'll learn

  • Amazon Braket
  • cloud quantum computing
  • AWS

Prerequisites

  • Basic Python (variables, functions, loops)
  • No quantum physics background needed

Amazon Braket is AWS’s managed quantum computing service. It gives you a single SDK that works across multiple simulators and hardware providers, so you can develop locally and then submit the same circuit to IonQ, Rigetti, IQM, or QuEra without rewriting code.

This tutorial covers installation, AWS setup, circuit building, simulator options, hardware submission, parameterized circuits, noise modeling, hybrid jobs, cost estimation, and common mistakes.

Why Amazon Braket?

Quantum computing hardware comes from many vendors, and each vendor traditionally ships its own SDK. If you want to run on IBM hardware, you use Qiskit. Google hardware uses Cirq. Rigetti has pyQuil. Each SDK has different circuit construction syntax, different result formats, and different authentication mechanisms. Comparing results across hardware technologies means learning multiple frameworks and maintaining separate codebases.

Braket solves this problem by providing a single SDK and a single API for all supported hardware. You write your circuit once using the Braket circuit API, then point it at any backend by changing a single device ARN string. The result object has the same structure regardless of whether the circuit ran on a local simulator, a managed cloud simulator, or a trapped-ion QPU.

The hardware providers currently available through Braket include:

  • IonQ (trapped-ion qubits): Harmony and Aria devices, known for high gate fidelity and all-to-all connectivity
  • Rigetti (superconducting qubits): Ankaa-2, offering fast gate speeds with nearest-neighbor connectivity
  • IQM (superconducting qubits): Garnet, a European provider with a 20-qubit processor
  • QuEra (neutral atom): Aquila, a special-purpose device for analog Hamiltonian simulation

Braket also provides unified billing through your AWS account. You do not need separate billing relationships with each hardware vendor. For research groups that want to benchmark algorithms across different qubit technologies, this is a significant operational simplification.

Installation

pip install amazon-braket-sdk

The local simulator works immediately after installation. No AWS credentials are required for local-only development.

For cloud simulators and hardware access, you also need the AWS CLI:

pip install awscli

Verify the installation:

import braket
print(braket.__version__)

AWS Account Setup

Many beginners hit roadblocks during AWS configuration. Follow these steps carefully.

Step 1: Create or use an existing AWS account

Go to aws.amazon.com and sign up. You need a credit card on file, but the local simulator costs nothing.

Step 2: Enable Amazon Braket in the console

Sign into the AWS Management Console, search for “Amazon Braket,” and open the service page. Braket must be explicitly enabled in your region before you can use it. Click the “Get Started” button and accept the service terms.

Step 3: Choose a supported region

Braket is not available in every AWS region. As of early 2026, the supported regions are:

  • us-east-1 (N. Virginia): most hardware providers available here
  • us-west-2 (Oregon): simulators and select hardware
  • eu-west-2 (London): simulators and IQM hardware

Choose us-east-1 if you want the widest hardware selection.

Step 4: Create an S3 bucket for results

Every cloud task (managed simulator or QPU) writes its results to an S3 bucket. Create a bucket in the same region where you plan to run Braket tasks:

aws s3 mb s3://my-braket-results-bucket --region us-east-1

The bucket name must be globally unique. If my-braket-results-bucket is taken, choose a different name.

Step 5: Configure IAM permissions

Your IAM user or role needs two sets of permissions:

  1. Braket permissions: braket:* (or use the managed policy AmazonBraketFullAccess)
  2. S3 permissions: s3:PutObject and s3:GetObject on your results bucket

A minimal IAM policy looks like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "braket:*",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject"],
      "Resource": "arn:aws:s3:::my-braket-results-bucket/*"
    }
  ]
}

Step 6: Configure local credentials

Run aws configure and enter your access key, secret key, and default region:

aws configure
# AWS Access Key ID: <your-key>
# AWS Secret Access Key: <your-secret>
# Default region name: us-east-1
# Default output format: json

Alternatively, set environment variables:

export AWS_ACCESS_KEY_ID=<your-key>
export AWS_SECRET_ACCESS_KEY=<your-secret>
export AWS_DEFAULT_REGION=us-east-1

Circuit Building Fundamentals

The Braket circuit API uses method chaining. Each gate method returns the circuit object itself, so you can chain calls together on a single line or split them across multiple lines. Both styles work.

Chained style

from braket.circuits import Circuit

circuit = Circuit().h(0).cnot(0, 1).h(1)

Multi-line style

from braket.circuits import Circuit

circuit = Circuit()
circuit.h(0)        # Hadamard on qubit 0
circuit.cnot(0, 1)  # CNOT with control=0, target=1
circuit.h(1)        # Hadamard on qubit 1

Both produce the same circuit. The multi-line style is easier to read for longer circuits; the chained style is convenient for short ones.

Qubit indexing

Braket uses plain integers for qubit indices. There is no separate register object like Qiskit’s QuantumRegister. Qubit 0 is the first qubit, qubit 1 is the second, and so on. Braket automatically allocates the number of qubits based on the highest index used in the circuit.

Gate names

Gates in Braket are lowercase methods, unlike Qiskit where many gates use uppercase (e.g., circuit.h() in Braket vs. circuit.h() in Qiskit, but circuit.cx() in Qiskit vs. circuit.cnot() in Braket). Here are the most commonly used gates:

GateMethodDescription
Hadamardh(qubit)Creates superposition
Pauli-Xx(qubit)Bit flip (NOT gate)
Pauli-Yy(qubit)Y rotation by pi
Pauli-Zz(qubit)Phase flip
CNOTcnot(control, target)Controlled-NOT
CZcz(control, target)Controlled-Z
SWAPswap(q0, q1)Swaps two qubits
Rxrx(angle, qubit)X-axis rotation by angle (radians)
Ryry(angle, qubit)Y-axis rotation by angle (radians)
Rzrz(angle, qubit)Z-axis rotation by angle (radians)
XXxx(angle, q0, q1)Ising XX coupling gate
YYyy(angle, q0, q1)Ising YY coupling gate
ZZzz(angle, q0, q1)Ising ZZ coupling gate
Tt(qubit)pi/4 phase gate
Ss(qubit)pi/2 phase gate

Note that rotation gates take the angle as the first argument, followed by the qubit index. This is different from Qiskit, where the qubit comes first in the circuit method call.

Your First Circuit: Bell State on the Local Simulator

from braket.circuits import Circuit
from braket.devices import LocalSimulator

# Create a Bell state circuit: (|00> + |11>) / sqrt(2)
circuit = Circuit()
circuit.h(0)       # Put qubit 0 into superposition
circuit.cnot(0, 1) # Entangle qubit 1 with qubit 0

print(circuit)

This prints the circuit diagram:

T  : |0|1|
q0 : -H-C-
         |
q1 : ---X-
T  : |0|1|

Reading the circuit diagram

Braket’s text diagram format works like this:

  • The T row at the top and bottom shows time steps (gate layers). |0|1| means there are two time steps: step 0 and step 1.
  • Each q row represents one qubit. q0 is qubit 0, q1 is qubit 1.
  • -H- is a Hadamard gate occupying one time step.
  • -C- is the control qubit of a CNOT gate. The vertical bar | connects it to -X- on the target qubit below.
  • --- represents an idle wire (the qubit is doing nothing during that time step).

Time flows left to right. Gates in the same column execute in the same time step (they operate on different qubits, so they can run in parallel).

Running on the Local Simulator

device = LocalSimulator()

task = device.run(circuit, shots=1000)
result = task.result()

counts = result.measurement_counts
print(counts)
# Counter({'11': 502, '00': 498})  -- approximate 50/50 split

With 1000 shots, you see roughly 500 outcomes of 00 and 500 of 11. This confirms the Bell state: the two qubits are always correlated (both 0 or both 1), but each individual measurement is random.

Statevector mode with shots=0

Setting shots=0 tells the simulator to return the exact statevector instead of sampling:

from braket.circuits import Circuit, result_types
from braket.devices import LocalSimulator

circuit = Circuit()
circuit.h(0)
circuit.cnot(0, 1)

# Request the statevector result type
circuit.state_vector()

device = LocalSimulator()
task = device.run(circuit, shots=0)
sv_result = task.result()

statevector = sv_result.result_types[0].value
print(statevector)
# [0.70710678+0j, 0.+0j, 0.+0j, 0.70710678+0j]

The statevector has four amplitudes corresponding to |00>, |01>, |10>, |11>. For the Bell state, only |00> and |11> have nonzero amplitudes of 1/sqrt(2), confirming the expected entangled state.

The Result Object in Depth

The result object returned by task.result() contains several useful attributes.

measurement_counts

A Python Counter mapping bitstrings to the number of times each outcome was observed:

result = device.run(circuit, shots=1000).result()
print(result.measurement_counts)
# Counter({'00': 510, '11': 490})

measurements

A NumPy array of shape (shots, n_qubits). Each row is one measurement outcome:

import numpy as np

print(result.measurements.shape)  # (1000, 2)
print(result.measurements[:5])
# [[0, 0], [1, 1], [0, 0], [1, 1], [0, 0]]

This is useful for custom post-processing where you need per-shot data.

measurement_probabilities

A dictionary mapping each observed bitstring to its relative frequency:

print(result.measurement_probabilities)
# {'00': 0.51, '11': 0.49}

Plotting a histogram

You can visualize the results with matplotlib:

import matplotlib.pyplot as plt

counts = result.measurement_counts
plt.bar(counts.keys(), counts.values())
plt.xlabel("Bitstring")
plt.ylabel("Counts")
plt.title("Bell State Measurement Results")
plt.show()

Building Larger Circuits: GHZ State

A GHZ (Greenberger-Horne-Zeilinger) state extends the Bell state to three or more qubits. It produces the state (|000…0> + |111…1>) / sqrt(2).

from braket.circuits import Circuit
from braket.devices import LocalSimulator

# Build a 5-qubit GHZ state
ghz = Circuit()
ghz.h(0)
for i in range(4):
    ghz.cnot(i, i + 1)

print(ghz)

device = LocalSimulator()
result = device.run(ghz, shots=2000).result()
print(result.measurement_counts)
# Counter({'00000': ~1000, '11111': ~1000})

Only two outcomes appear: all zeros and all ones. Any other outcome would indicate an error in the circuit or noise in the hardware.

Parameterized Circuits with FreeParameter

For variational algorithms like VQE and QAOA, you need to run the same circuit structure at many different parameter values. Braket’s FreeParameter lets you define a circuit with symbolic parameters, compile it once, and then substitute concrete values at runtime.

from braket.circuits import Circuit, FreeParameter
from braket.devices import LocalSimulator

# Define symbolic parameters
theta = FreeParameter("theta")
phi = FreeParameter("phi")

# Build a parameterized circuit
circuit = Circuit()
circuit.ry(theta, 0)    # Ry rotation by theta on qubit 0
circuit.cnot(0, 1)      # Entangle qubits 0 and 1
circuit.rz(phi, 1)      # Rz rotation by phi on qubit 1

print(circuit)

To run the circuit, substitute values for the parameters:

device = LocalSimulator()

# Run with specific parameter values
bound_circuit = circuit(theta=0.5, phi=1.2)
result = device.run(bound_circuit, shots=1000).result()
print(result.measurement_counts)

You can sweep across multiple parameter values in a loop:

import numpy as np

device = LocalSimulator()
theta_values = np.linspace(0, np.pi, 10)

for theta_val in theta_values:
    bound = circuit(theta=theta_val, phi=0.0)
    result = device.run(bound, shots=1000).result()
    prob_00 = result.measurement_probabilities.get("00", 0)
    print(f"theta={theta_val:.2f}, P(00)={prob_00:.3f}")

This pattern is critical for VQE, where a classical optimizer repeatedly evaluates the circuit at different parameter values to minimize an energy expectation value. FreeParameter avoids rebuilding the circuit object on each iteration.

Switching to Managed Simulators

When your circuit grows beyond what your laptop can handle, switch to one of Braket’s managed simulators. These run on AWS infrastructure and support larger qubit counts.

SV1: Statevector Simulator

SV1 simulates quantum circuits by tracking the full statevector. It handles up to 34 qubits, but memory usage grows exponentially (2^n complex amplitudes). SV1 works well for any circuit structure, including deeply entangled circuits.

from braket.aws import AwsDevice

# SV1: fully managed statevector simulator
device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")

task = device.run(circuit(theta=0.5, phi=1.2), shots=1000,
                  s3_destination_folder=("my-braket-results-bucket", "sv1-results/"))
print(f"Task ID: {task.id}")

# Blocks until the task completes (usually seconds to minutes)
result = task.result()
print(result.measurement_counts)

You must provide an S3 bucket in the same region as your Braket service. Braket writes task results there and retains them for 90 days.

TN1: Tensor Network Simulator

TN1 uses tensor network contraction to simulate circuits. It handles up to 50 qubits, but performance depends heavily on the circuit structure.

When to use TN1 over SV1:

  • Your circuit has many qubits (35-50) but relatively few layers of gates
  • Your circuit has limited entanglement (not every qubit is entangled with every other)
  • You are studying circuits that resemble real-world NISQ algorithms, which tend to be wide and shallow

When TN1 struggles:

  • Deeply entangled circuits where the bond dimension grows large
  • A depth-50 circuit on 50 qubits with maximal entanglement may be intractable
  • Circuits with many long-range CNOT gates that create high entanglement across the tensor network

Rule of thumb: if your circuit is wide (many qubits) but shallow (few gate layers), try TN1 first. If the circuit is deep and heavily entangled, use SV1 (up to 34 qubits).

# TN1: tensor network simulator, up to 50 qubits
device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/tn1")

# A wide, shallow circuit that TN1 handles well
wide_circuit = Circuit()
for i in range(50):
    wide_circuit.h(i)
for i in range(0, 49, 2):
    wide_circuit.cnot(i, i + 1)

task = device.run(wide_circuit, shots=1000,
                  s3_destination_folder=("my-braket-results-bucket", "tn1-results/"))
result = task.result()
print(result.measurement_counts)

DM1: Density Matrix Simulator with Noise

DM1 simulates circuits using the density matrix formalism, which naturally supports noise modeling. It handles up to 17 qubits (density matrices require 2^(2n) memory, so the qubit limit is lower than SV1).

Noise Modeling with DM1

Real quantum hardware introduces errors. DM1 lets you simulate these errors so you can test whether your circuit produces useful results under realistic noise conditions.

Adding depolarizing noise

Depolarizing noise randomly applies one of the Pauli operators (X, Y, Z) with some probability after each gate. This models a common type of hardware error.

from braket.circuits import Circuit
from braket.circuits.noises import Depolarizing
from braket.devices import LocalSimulator

# Build a Bell state circuit
circuit = Circuit()
circuit.h(0)
circuit.cnot(0, 1)

# Apply depolarizing noise to every gate in the circuit
# probability=0.01 means a 1% chance of error per gate
circuit.apply_gate_noise(Depolarizing(probability=0.01))

# Use the local density matrix simulator
device = LocalSimulator("braket_dm")
result = device.run(circuit, shots=1000).result()
print(result.measurement_counts)
# Counter({'00': 485, '11': 480, '01': 18, '10': 17})

Notice that 01 and 10 outcomes appear, even though a perfect Bell state only produces 00 and 11. This is the noise introducing errors.

Adding bit-flip noise

Bit-flip noise flips a qubit’s state with some probability. You can apply it selectively to specific gates:

from braket.circuits import Circuit
from braket.circuits.noises import BitFlip

circuit = Circuit()
circuit.h(0)
circuit.cnot(0, 1)

# Apply bit-flip noise only to CNOT gates
circuit.apply_gate_noise(BitFlip(probability=0.02), target_gates=[Circuit().cnot])

device = LocalSimulator("braket_dm")
result = device.run(circuit, shots=1000).result()
print(result.measurement_counts)

Using the managed DM1 simulator

For larger noisy simulations (up to 17 qubits), use the managed DM1 service:

from braket.aws import AwsDevice

device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/dm1")

task = device.run(circuit, shots=1000,
                  s3_destination_folder=("my-braket-results-bucket", "dm1-results/"))
result = task.result()
print(result.measurement_counts)

DM1 gives you realistic noisy results without consuming hardware time. Use it to estimate whether your algorithm tolerates typical error rates before spending money on QPU access.

Hardware: Submitting to a QPU

Submitting to real hardware follows the same pattern as simulators. The key differences are longer queue times (minutes to hours), per-shot billing, and the requirement that shots be a positive integer (no statevector mode on real hardware).

from braket.aws import AwsDevice
from braket.circuits import Circuit

# Build a Bell state
circuit = Circuit().h(0).cnot(0, 1)

# IonQ Aria -- 25-qubit trapped-ion QPU
device = AwsDevice("arn:aws:braket:us-east-1::device/qpu/ionq/Aria-1")

# Check if the device is currently online
print(f"Available: {device.is_available}")

# Submit the task
task = device.run(circuit, shots=100,
                  s3_destination_folder=("my-braket-results-bucket", "ionq-results/"))
print(f"Task ARN: {task.id}")
print(f"Status: {task.state()}")

# Block until the task completes (this can take minutes to hours)
result = task.result()
print(result.measurement_counts)

Checking Device Status and Properties

Real quantum hardware operates on scheduled availability windows. Before submitting a task, check whether the device is online and review its properties.

from braket.aws import AwsDevice

device = AwsDevice("arn:aws:braket:us-east-1::device/qpu/ionq/Aria-1")

# Is the device currently accepting tasks?
print(f"Available: {device.is_available}")

# Device properties: native gates, qubit count, connectivity
props = device.properties.dict()
print(f"Qubit count: {props['paradigm']['qubitCount']}")
print(f"Native gates: {props['paradigm']['nativeGateSet']}")

To list all currently online devices:

from braket.aws import AwsDevice

devices = AwsDevice.get_devices(statuses=["ONLINE"])
for d in devices:
    print(f"{d.name:20s} {d.provider_name:10s} {d.arn}")

Devices that are offline will queue your task and execute it during the next availability window. Check the Braket console for the device’s schedule and typical queue depths.

Available Devices (as of early 2026)

ProviderDeviceTechnologyMax QubitsRegion
IonQAria-1Trapped ion25us-east-1
RigettiAnkaa-2Superconducting84us-west-2
IQMGarnetSuperconducting20eu-west-2
QuEraAquilaNeutral atom256us-east-1

QuEra’s Aquila device is special-purpose: it runs analog Hamiltonian simulation rather than gate-based circuits. It uses a different programming model (AHS programs instead of circuit objects).

Use AwsDevice.get_devices() to check the current list, as hardware availability changes over time.

Cost Estimation

Understanding Braket pricing helps you avoid surprises.

Simulator pricing

SimulatorPer-task feePer-shot fee
Local simulatorFreeFree
SV1$0.075$0.00075 per qubit per circuit
TN1$0.275$0.00145 per qubit per circuit
DM1$0.075$0.00075 per qubit per circuit

QPU pricing

DevicePer-task feePer-shot fee
IonQ Aria$0.30$0.01
Rigetti Ankaa-2$0.30$0.0009
IQM Garnet$0.30$0.00145

Example cost calculation

Running a 100-shot Bell state on IonQ Aria:

  • Per-task fee: $0.30
  • Per-shot cost: 100 shots x 0.01=0.01 = 1.00
  • Total: $1.30

Running the same circuit on Rigetti Ankaa-2:

  • Per-task fee: $0.30
  • Per-shot cost: 100 shots x 0.0009=0.0009 = 0.09
  • Total: $0.39

The per-shot cost difference reflects the underlying hardware economics: trapped-ion gates are slower but more accurate, while superconducting gates are faster but noisier.

Always check the AWS Braket pricing page for current rates.

Hybrid Jobs for Variational Algorithms

Variational algorithms like VQE run a classical-quantum loop: a classical optimizer proposes parameters, the quantum circuit evaluates an energy, and the optimizer uses that result to propose better parameters. This loop can require hundreds or thousands of circuit evaluations.

Braket Hybrid Jobs runs these workloads efficiently by:

  • Co-locating compute: the classical optimizer runs on an EC2 instance near the QPU, reducing network latency between iterations
  • Checkpointing: progress is saved between iterations, so a failed step can resume without starting over
  • Spot pricing: the classical compute portion can use spot instances to reduce costs
  • Priority queuing: tasks submitted from a Hybrid Job get priority access to the QPU

Minimal Hybrid Job example

First, create a Python script that defines your algorithm:

# algorithm_script.py
import numpy as np
from braket.aws import AwsDevice
from braket.circuits import Circuit, FreeParameter
from braket.jobs import save_job_result

def main():
    device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")
    theta = FreeParameter("theta")

    # Simple circuit: Ry rotation followed by measurement
    circuit = Circuit().ry(theta, 0)

    # Optimization loop (simplified)
    best_theta = 0.0
    best_cost = float("inf")

    for theta_val in np.linspace(0, np.pi, 20):
        bound_circuit = circuit(theta=theta_val)
        result = device.run(bound_circuit, shots=100,
                            s3_destination_folder=("my-braket-results-bucket", "hybrid/")).result()
        # Use P(|1>) as the cost function
        prob_1 = result.measurement_probabilities.get("1", 0)
        if prob_1 < best_cost:
            best_cost = prob_1
            best_theta = theta_val

    save_job_result({"best_theta": best_theta, "best_cost": best_cost})

if __name__ == "__main__":
    main()

Then launch the Hybrid Job:

from braket.aws import AwsQuantumJob

job = AwsQuantumJob.create(
    device="arn:aws:braket:::device/quantum-simulator/amazon/sv1",
    source_module="algorithm_script.py",
    role_arn="arn:aws:iam::123456789012:role/AmazonBraketJobsExecutionRole",
    s3_destination_folder=("my-braket-results-bucket", "jobs/"),
)

print(f"Job ARN: {job.arn}")
print(f"Status: {job.state()}")

# Wait for completion and retrieve results
result = job.result()
print(result)

For production VQE workloads, replace the simple grid search with scipy.optimize.minimize or another classical optimizer, and replace the toy circuit with a problem-specific ansatz.

Local vs Managed vs Hybrid Jobs

FeatureLocal SimulatorManaged SimulatorQPUHybrid Jobs
CostFreePer-task billingPer-shot billingCompute + QPU
Max qubits~2534 (SV1), 50 (TN1), 17 (DM1)Device-specificDevice-specific
AWS account neededNoYesYesYes
Queue timeNoneSecondsMinutes to hoursPriority access
Best forDevelopment, testingValidation, large circuitsFinal resultsVariational loops

Development workflow: start with the local simulator to verify circuit logic, move to SV1 or DM1 for larger or noisy simulations, and only submit to QPU hardware when you need real-device results.

Common Mistakes

S3 bucket in the wrong region

If your S3 bucket is in us-west-2 but you submit a task to a device in us-east-1, you get a “No such bucket” or access error. The bucket and the Braket service must be in the same region.

Fix: create a separate bucket in each region you plan to use, or consolidate your work to one region.

Missing IAM permissions

An AccessDeniedException usually means your IAM user or role is missing either braket:* permissions or S3 write access on the results bucket. You need both.

Fix: attach the AmazonBraketFullAccess managed policy and add S3 permissions for your specific bucket.

Using shots=0 on a QPU

QPU devices require a positive integer for the shots parameter. Setting shots=0 (statevector mode) only works on simulators. Real hardware always produces sampled measurement outcomes.

Fix: always pass shots=100 or another positive integer when targeting hardware.

Not understanding the asynchronous model

When you call device.run(circuit, shots=100), the task is submitted asynchronously. Calling task.result() blocks your Python process until the task completes. For QPU tasks, this can take minutes to hours depending on queue depth.

Fix: for long-running tasks, save the task ID and poll later:

task = device.run(circuit, shots=100,
                  s3_destination_folder=("my-braket-results-bucket", "results/"))
task_id = task.id
print(f"Task submitted: {task_id}")

# Later, retrieve the result using the task ID
from braket.aws import AwsQuantumTask
recovered_task = AwsQuantumTask(arn=task_id)
print(f"Status: {recovered_task.state()}")
if recovered_task.state() == "COMPLETED":
    result = recovered_task.result()
    print(result.measurement_counts)

Unsupported gates on target hardware

Each QPU has a native gate set. If your circuit uses a gate that the target device does not support natively, Braket automatically decomposes it into native gates. This decomposition adds extra gates, increasing circuit depth and introducing more noise.

Fix: check the device’s native gate set before designing your circuit:

device = AwsDevice("arn:aws:braket:us-east-1::device/qpu/ionq/Aria-1")
props = device.properties.dict()
print(f"Native gates: {props['paradigm']['nativeGateSet']}")

Then design your circuit using those gates when possible. For IonQ devices, the native gates typically include GPi, GPi2, and MS (Molmer-Sorensen). For Rigetti devices, native gates are typically Rx, Rz, and CZ.

What to Try Next

  • Build a quantum teleportation circuit and verify the results match theory
  • Implement a simple VQE for the hydrogen molecule using FreeParameter and scipy.optimize.minimize
  • Compare results from the local simulator, DM1 with noise, and a real QPU to see how noise affects your circuit
  • Explore QuEra Aquila for analog Hamiltonian simulation (a different programming model from gate-based circuits)
  • See the Braket Reference for the full gate set and result type API

Was this tutorial helpful?