

# Building your quantum tasks with Amazon Braket
<a name="braket-build"></a>

Braket provides fully managed Jupyter notebook environments that make it straightforward to get started. Braket notebooks are pre-installed with sample algorithms, resources, and developer tools, including the Amazon Braket SDK. With the Amazon Braket SDK, you can build quantum algorithms and then test and run them on different quantum computers and simulators by changing a single line of code.

**Topics**
+ [Building your first circuit](braket-get-started-run-circuit.md)
+ [Getting Expert advice](braket-expert-advice.md)
+ [Run your circuits with OpenQASM 3.0](braket-openqasm.md)
+ [Explore Experimental Capabilities](braket-experimental-capabilities.md)
+ [Pulse control on Amazon Braket](braket-pulse-control.md)
+ [Analog Hamiltonian Simulation](braket-analog-hamiltonian-simulation.md)
+ [Working with AWS Boto3](braket-using-boto3.md)

# Building your first circuit
<a name="braket-get-started-run-circuit"></a>

After your notebook instance has launched, open the instance with a standard Jupyter interface by choosing the notebook you just created.

![\[Notebooks interface showing an existing notebook instance named amazon-braket-test with status InService and its URL.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/console-page.png)


Amazon Braket notebook instances are pre-installed with the Amazon Braket SDK and all its dependencies. Start by creating a new notebook with `conda_braket` kernel.

![\[Launcher interface for Notebooks, Console, and Other tools like Terminal, Text File, Markdown File, Python File, with highlighted conda_braket Python environment.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/jupyter-open.png)


You can start with a simple “Hello, world\$1” example. First, construct a circuit that prepares a Bell state, and then run that circuit on different devices to obtain the results.

Begin by importing the Begin by importing the Amazon Braket SDK modules and defining a simpleBRAKETlong; SDK modules and defining a basic Bell State circuit.

```
import boto3
from braket.aws import AwsDevice
from braket.devices import LocalSimulator
from braket.circuits import Circuit

# Create the circuit
bell = Circuit().h(0).cnot(0, 1)
```

You can visualize the circuit with this command:

```
print(bell)
```

```
T  : │  0  │  1  │
      ┌───┐       
q0 : ─┤ H ├───●───
      └───┘   │   
            ┌─┴─┐ 
q1 : ───────┤ X ├─
            └───┘ 
T  : │  0  │  1  │
```

 **Run your circuit on the local simulator** 

Next, choose the quantum device on which to run the circuit. The Amazon Braket SDK comes with a local simulator for rapid prototyping and testing. We recommend using the local simulator for smaller circuits, which can be up to 25 qubits (depending on your local hardware).

To instantiate the local simulator:

```
# Instantiate the local simulator
local_sim = LocalSimulator()
```

and run the circuit:

```
# Run the circuit
result = local_sim.run(bell, shots=1000).result()
counts = result.measurement_counts
print(counts)
```

You should see a result something like this:

```
Counter({'11': 503, '00': 497})
```

The specific Bell state you have prepared is an equal superposition of \$100⟩ and \$111⟩, and an about equal (up to shot noise) distribution of 00 and 11 as measurement outcomes, as expected.

 **Run your circuit on an on-demand simulator** 

Amazon Braket also provides access to an on-demand, high-performance simulator, SV1, for running larger circuits. SV1 is an on-demand state-vector simulator that allows for simulation of quantum circuits of up to 34 qubits. You can find more information on SV1 in the [Supported Devices](braket-devices.md) section and in the AWS console. When running quantum tasks on SV1 (and on TN1 or any QPU), the results of your quantum task are stored in an S3 bucket in your account. If you do not specify a bucket, the Braket SDK creates a default bucket `amazon-braket-{region}-{accountID}` for you. To learn more, see [Managing access to Amazon Braket ](braket-manage-access.md).

**Note**  
Fill in your actual, existing bucket name where the following example shows `amazon-braket-s3-demo-bucket` as your bucket name. Bucket names for Amazon Braket always begin with `amazon-braket-` followed by other identifying characters you add. If you need information on how to set up an S3 bucket, see [Getting started with Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/GetStartedWithS3.html).

```
# Get the account ID
aws_account_id = boto3.client("sts").get_caller_identity()["Account"]

# The name of the bucket
my_bucket = "amazon-braket-s3-demo-bucket"

# The name of the folder in the bucket
my_prefix = "simulation-output"
s3_folder = (my_bucket, my_prefix)
```

To run a circuit on SV1, you must provide the location of the S3 bucket you previously selected as a positional argument in the `.run()` call.

```
# Choose the cloud-based on-demand simulator to run your circuit
device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")

# Run the circuit
task = device.run(bell, s3_folder, shots=100)

# Display the results
print(task.result().measurement_counts)
```

The Amazon Braket console provides further information about your quantum task. Navigate to the **Quantum Tasks** tab in the console and your quantum task should be on the top of the list. Alternatively, you can search for your quantum task using the unique quantum task ID or other criteria.

**Note**  
After 90 days, Amazon Braket automatically removes all quantum task IDs and other metadata associated with your quantum tasks. For more information, see [Data retention](https://docs.aws.amazon.com/braket/latest/developerguide/security.html#braket-data-retention).

 **Running on a QPU** 

With Amazon Braket, you can run the previous quantum circuit example on a physical quantum computer by just changing a single line of code. Amazon Braket provides access to a variety of Quantum processing unit (QPU) devices. You can find information about the different devices and availability windows in the [Supported Devices](braket-devices.md) section, and in the AWS console under the **Devices** tab. The following example shows how to instantiate an IQM device.

```
# Choose the IQM hardware to run your circuit
device = AwsDevice("arn:aws:braket:eu-north-1::device/qpu/iqm/Garnet")
```

Or choose an IonQ device with this code:

```
# Choose the Ionq device to run your circuit
device = AwsDevice("arn:aws:braket:us-east-1::device/qpu/ionq/Aria-1")
```

After selecting a device and before running your workload, you can query device queue depth with the following code to determine the number of quantum tasks or hybrid jobs. Additionally, customers can view device specific queue depths on the Devices page of the Amazon Braket Management Console. 

```
# Print your queue depth
print(device.queue_depth().quantum_tasks)
# Returns the number of quantum tasks queued on the device
# {<QueueType.NORMAL: 'Normal'>: '0', <QueueType.PRIORITY: 'Priority'>: '0'}

print(device.queue_depth().jobs)
# Returns the number of hybrid jobs queued on the device
# '2'
```

When you run your task, the Amazon Braket SDK polls for a result (with a default timeout of 5 days). You can change this default by modifying the `poll_timeout_seconds` parameter in the the `.run()` command as shown in the example that follows. Keep in mind that if your polling timeout is too short, results may not be returned within the polling time, such as when a QPU is unavailable and a local timeout error is returned. You can restart the polling by calling the `task.result()` function.

```
# Define quantum task with 1 day polling timeout
task = device.run(bell, s3_folder, poll_timeout_seconds=24*60*60)
print(task.result().measurement_counts)
```

Additionally, after submitting your quantum task or hybrid job, you can call the `queue_position()` function to check your queue position.

```
print(task.queue_position().queue_position)
# Return the number of quantum tasks queued ahead of you
# '2'
```

# Building your first quantum algorithms
<a name="braket-explore-algorithm-library"></a>

The Amazon Braket algorithm library is a catalog of pre-built quantum algorithms written in Python. Run these algorithms as they are, or use them as starting points for building more complex algorithms. You can access the algorithm library from the Braket console. For more information, see the [Braket Github algorithm library](https://github.com/aws-samples/amazon-braket-algorithm-library).

![\[Amazon Braket's Algorithm library page showing a list of pre-built quantum algorithms like Berstein Vazirani, Deutsch-Jozsa, Grover's algorithm, and Quantum Approximate Optimization Algorithm with brief descriptions.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/AlgorithmLibrary.png)


The Braket console provides a description of each available algorithm in the algorithm library. Choose a GitHub link to see the details of each algorithm, or choose **Open notebook** to open or create a notebook that contains all of the available algorithms. If you choose the notebook option, you can then find the Braket algorithm library in the root folder of your notebook.

# Constructing circuits in the SDK
<a name="braket-constructing-circuit"></a>

This section provides examples of defining a circuit, viewing available gates, extending a circuit, and viewing gates that each device supports. It also contains instructions on how to manually allocate qubits, instruct the compiler to run your circuits exactly as defined, and build noisy circuits with a noise simulator.

You can also work at the pulse level in Braket for various gates with certain QPUs. For more information, see [Pulse Control on Amazon Braket](braket-pulse-control.md).

**Topics**
+ [Gates and circuits](#braket-gates)
+ [Program sets](#braket-program-set)
+ [Partial measurement](#braket-partial-measurement)
+ [Manual qubit allocation](#manual-qubit-allocation)
+ [Verbatim compilation](#verbatim-compilation)
+ [Noise simulation](#noise-simulation)

## Gates and circuits
<a name="braket-gates"></a>

Quantum gates and circuits are defined in the [https://github.com/aws/amazon-braket-sdk-python/blob/main/src/braket/circuits/circuit.py](https://github.com/aws/amazon-braket-sdk-python/blob/main/src/braket/circuits/circuit.py) class of the Amazon Braket Python SDK. From the SDK, you can instantiate a new circuit object by calling `Circuit()`.

 **Example: Define a circuit** 

The example starts by defining a sample circuit of four qubits (labelled `q0`, `q1`, `q2`, and `q3`) consisting of standard, single-qubit Hadamard gates and two-qubit CNOT gates. You can visualize this circuit by calling the `print` function as the following example shows.

```
# Import the circuit module
from braket.circuits import Circuit

# Define circuit with 4 qubits
my_circuit = Circuit().h(range(4)).cnot(control=0, target=2).cnot(control=1, target=3)
print(my_circuit)
```

```
T  : │  0  │     1     │
      ┌───┐             
q0 : ─┤ H ├───●─────────
      └───┘   │         
      ┌───┐   │         
q1 : ─┤ H ├───┼─────●───
      └───┘   │     │   
      ┌───┐ ┌─┴─┐   │   
q2 : ─┤ H ├─┤ X ├───┼───
      └───┘ └───┘   │   
      ┌───┐       ┌─┴─┐ 
q3 : ─┤ H ├───────┤ X ├─
      └───┘       └───┘ 
T  : │  0  │     1     │
```

 **Example: Define a parameterized circuit** 

In this example, we define a circuit with gates that depend on free parameters. We can specify the values of these parameters to create a new circuit, or, when submitting the circuit, to run as a quantum task on certain devices.

```
from braket.circuits import Circuit, FreeParameter

# Define a FreeParameter to represent the angle of a gate
alpha = FreeParameter("alpha")

# Define a circuit with three qubits
my_circuit = Circuit().h(range(3)).cnot(control=0, target=2).rx(0, alpha).rx(1, alpha)
print(my_circuit)
```

You can create a new, non-parameterized circuit from a parametrized one by supplying either a single `float` (which is the value all free parameters will take) or keyword arguments specifying each parameter's value to the circuit as follows.

```
my_fixed_circuit = my_circuit(1.2)
my_fixed_circuit = my_circuit(alpha=1.2)
print(my_fixed_circuit)
```

Note that `my_circuit` is unmodified, so you can use it to instantiate many new circuits with fixed parameter values.

 **Example: Modify gates in a circuit** 

The following example defines a circuit with gates that use control and power modifiers. You can use these modifications to create new gates, such as the controlled `Ry` gate.

```
from braket.circuits import Circuit

# Create a bell circuit with a controlled x gate
my_circuit = Circuit().h(0).x(control=0, target=1)

# Add a multi-controlled Ry gate of angle .13
my_circuit.ry(angle=.13, target=2, control=(0, 1))

# Add a 1/5 root of X gate
my_circuit.x(0, power=1/5)

print(my_circuit)
```

Gate modifiers are supported only on the local simulator.

 **Example: See all available gates** 

The following example shows how to look at all the available gates in Amazon Braket.

```
from braket.circuits import Gate
# Print all available gates in Amazon Braket
gate_set = [attr for attr in dir(Gate) if attr[0].isupper()]
print(gate_set)
```

The output from this code lists all of the gates.

```
['CCNot', 'CNot', 'CPhaseShift', 'CPhaseShift00', 'CPhaseShift01', 'CPhaseShift10', 'CSwap', 'CV', 'CY', 'CZ', 'ECR', 'GPhase', 'GPi', 'GPi2', 'H', 'I', 'ISwap', 'MS', 'PRx', 'PSwap', 'PhaseShift', 'PulseGate', 'Rx', 'Ry', 'Rz', 'S', 'Si', 'Swap', 'T', 'Ti', 'U', 'Unitary', 'V', 'Vi', 'X', 'XX', 'XY', 'Y', 'YY', 'Z', 'ZZ']
```

Any of these gates can be appended to a circuit by calling the method for that type of circuit. For example, call `circ.h(0)`, to add a Hadamard gate to the first qubit.

**Note**  
Gates are appended in place, and the example that follows adds all of the gates listed in the previous example to the same circuit.

```
circ = Circuit()
# toffoli gate with q0, q1 the control qubits and q2 the target.
circ.ccnot(0, 1, 2)
# cnot gate
circ.cnot(0, 1)
# controlled-phase gate that phases the |11> state, cphaseshift(phi) = diag((1,1,1,exp(1j*phi))), where phi=0.15 in the examples below
circ.cphaseshift(0, 1, 0.15)
# controlled-phase gate that phases the |00> state, cphaseshift00(phi) = diag([exp(1j*phi),1,1,1])
circ.cphaseshift00(0, 1, 0.15)
# controlled-phase gate that phases the |01> state, cphaseshift01(phi) = diag([1,exp(1j*phi),1,1])
circ.cphaseshift01(0, 1, 0.15)
# controlled-phase gate that phases the |10> state, cphaseshift10(phi) = diag([1,1,exp(1j*phi),1])
circ.cphaseshift10(0, 1, 0.15)
# controlled swap gate
circ.cswap(0, 1, 2)
# swap gate
circ.swap(0,1)
# phaseshift(phi)= diag([1,exp(1j*phi)])
circ.phaseshift(0,0.15)
# controlled Y gate
circ.cy(0, 1)
# controlled phase gate
circ.cz(0, 1)
# Echoed cross-resonance gate applied to q0, q1
circ = Circuit().ecr(0,1)
# X rotation with angle 0.15
circ.rx(0, 0.15)
# Y rotation with angle 0.15
circ.ry(0, 0.15)
# Z rotation with angle 0.15
circ.rz(0, 0.15)
# Hadamard gates applied to q0, q1, q2
circ.h(range(3))
# identity gates applied to q0, q1, q2
circ.i([0, 1, 2])
# iswap gate, iswap = [[1,0,0,0],[0,0,1j,0],[0,1j,0,0],[0,0,0,1]]
circ.iswap(0, 1)
# pswap gate, PSWAP(phi) = [[1,0,0,0],[0,0,exp(1j*phi),0],[0,exp(1j*phi),0,0],[0,0,0,1]]
circ.pswap(0, 1, 0.15)
# X gate applied to q1, q2
circ.x([1, 2])
# Y gate applied to q1, q2
circ.y([1, 2])
# Z gate applied to q1, q2
circ.z([1, 2])
# S gate applied to q0, q1, q2
circ.s([0, 1, 2])
# conjugate transpose of S gate applied to q0, q1
circ.si([0, 1])
# T gate applied to q0, q1
circ.t([0, 1])
# conjugate transpose of T gate applied to q0, q1
circ.ti([0, 1])
# square root of not gate applied to q0, q1, q2
circ.v([0, 1, 2])
# conjugate transpose of square root of not gate applied to q0, q1, q2
circ.vi([0, 1, 2])
# exp(-iXX theta/2)
circ.xx(0, 1, 0.15)
# exp(i(XX+YY) theta/4), where theta=0.15 in the examples below
circ.xy(0, 1, 0.15)
# exp(-iYY theta/2)
circ.yy(0, 1, 0.15)
# exp(-iZZ theta/2)
circ.zz(0, 1, 0.15)
# IonQ native gate GPi with angle 0.15 applied to q0
circ.gpi(0, 0.15)
# IonQ native gate GPi2 with angle 0.15 applied to q0
circ.gpi2(0, 0.15)
# IonQ native gate MS with angles 0.15, 0.15, 0.15 applied to q0, q1
circ.ms(0, 1, 0.15, 0.15, 0.15)
```

Apart from the pre-defined gate set, you also can apply self-defined unitary gates to the circuit. These can be single-qubit gates (as shown in the following source code) or multi-qubit gates applied to the qubits defined by the `targets` parameter.

```
import numpy as np

# Apply a general unitary
my_unitary = np.array([[0, 1],[1, 0]])
circ.unitary(matrix=my_unitary, targets=[0])
```

 **Example: Extend existing circuits** 

You can extend existing circuits by adding instructions. An `Instruction` is a quantum directive that describes the quantum task to perform on a quantum device. `Instruction` operators include objects of type `Gate` only.

```
# Import the Gate and Instruction modules
from braket.circuits import Gate, Instruction

# Add instructions directly.
circ = Circuit([Instruction(Gate.H(), 4), Instruction(Gate.CNot(), [4, 5])])

# Or with add_instruction/add functions
instr = Instruction(Gate.CNot(), [0, 1])
circ.add_instruction(instr)
circ.add(instr)

# Specify where the circuit is appended
circ.add_instruction(instr, target=[3, 4])
circ.add_instruction(instr, target_mapping={0: 3, 1: 4})

# Print the instructions
print(circ.instructions)
# If there are multiple instructions, you can print them in a for loop
for instr in circ.instructions:
     print(instr)

# Instructions can be copied
new_instr = instr.copy()
# Appoint the instruction to target
new_instr = instr.copy(target=[5, 6])
new_instr = instr.copy(target_mapping={0: 5, 1: 6})
```

 **Example: View the gates that each device supports** 

Simulators support all gates in the Braket SDK, but QPU devices support a smaller subset. You can find the supported gates of a device in the device properties. The following shows an example with an IonQ device:

```
# Import the device module
from braket.aws import AwsDevice

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

# Get device name
device_name = device.name
# Show supportedQuantumOperations (supported gates for a device)
device_operations = device.properties.dict()['action']['braket.ir.openqasm.program']['supportedOperations']
print('Quantum Gates supported by {}:\n {}'.format(device_name, device_operations))
```

```
Quantum Gates supported by Aria 1:
 ['x', 'y', 'z', 'h', 's', 'si', 't', 'ti', 'v', 'vi', 'rx', 'ry', 'rz', 'cnot', 'swap', 'xx', 'yy', 'zz']
```

Supported gates may need to be compiled into native gates before they can run on quantum hardware. When you submit a circuit, Amazon Braket performs this compilation automatically.

 **Example: Programmatically retrieve the fidelity of native gates supported by a device** 

You can view the fidelity information on the **Devices** page of the Braket console. Sometimes it is helpful to access the same information programmatically. The following code shows how to extract the two qubit gate fidelity between two gates of a QPU.

```
# Import the device module
from braket.aws import AwsDevice
 
device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3") 

# Specify the qubits 
a=10 
b=11
edge_properties_entry = device.properties.standardized.twoQubitProperties['10-11'].twoQubitGateFidelity
gate_name = edge_properties_entry[0].gateName
fidelity = edge_properties_entry[0].fidelity
print(f"Fidelity of the {gate_name} gate between qubits {a} and {b}: {fidelity}")
```

## Program sets
<a name="braket-program-set"></a>

Program sets efficiently run multiple quantum circuits in a single quantum task. In that one task, you can submit up to 100 quantum circuits or a single parametric circuit with up to 100 different parameter sets. This operation minimizes the time between subsequent circuit executions and reduces quantum task processing overhead. Currently, program sets are supported on the Amazon Braket Local Simulator and on AQT, IQM and Rigetti devices.

**Defining a ProgramSet**

The following first code example demonstrates how to build a `ProgramSet` using both parameterized circuits and circuits without parameters.

```
from braket.aws import AwsDevice
from braket.circuits import Circuit, FreeParameter
from braket.program_sets.circuit_binding import CircuitBinding
from braket.program_sets import ProgramSet

# Initialize the quantum device
device = AwsDevice("arn:aws:braket:eu-north-1::device/qpu/iqm/Garnet")

# Define circuits
circ1 = Circuit().h(0).cnot(0, 1)
circ2 = Circuit().rx(0, 0.785).ry(1, 0.393).cnot(1, 0)
circ3 = Circuit().t(0).t(1).cz(0, 1).s(0).cz(1, 2).s(1).s(2)
parameterize_circuit = Circuit().rx(0, FreeParameter("alpha")).cnot(0, 1).ry(1, FreeParameter("beta"))

# Create circuit bindings with different parameters
circuit_binding = CircuitBinding(
    circuit=parameterize_circuit,
    input_sets={
            'alpha': (0.10, 0.11, 0.22, 0.34, 0.45),
            'beta': (1.01, 1.01, 1.03, 1.04, 1.04),
    })

# Creating the program set
program_set_1 = ProgramSet([
    circ1,
    circ2,
    circ3,
    circuit_binding,
])
```

This program set contains four unique programs: `circ1`, `circ2`, `circ3`, and `circuit_binding`. The `circuit_binding` program runs with five different parameter bindings, creating five executables. The other three parameter-free programs create one executable each. This results in eight total executables, as shown in the following image.

![\[ProgramSet structure with four circuits, where c4 uses CircuitBinding for processing five input sets.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/program_set1.png)


The following second code example demonstrates how to use the `product()` method to attach the same set of observables to each executable of the program set.

```
from braket.circuits.observables import I, X, Y, Z

observables = [Z(0) @ Z(1), X(0) @ X(1), Z(0) @ X(1), X(0) @ Z(1)]

program_set_2 = ProgramSet.product(
    circuits=[circ1, circ2, circuit_binding],
    observables=observables
)
```

For parameter-free programs, each observable is measured for each circuit. For parametric programs, each observable is measured for each input set, as shown in the following image.

![\[ProgramSet.product showing parallel execution of three circuits, with c3 using CircuitBinding to process five input sets with five observables each.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/program_set2.png)


The following third code example demonstrates how to use the `zip()` method to pair individual observables with specific parameter sets in the `ProgramSet`.

```
program_set_3 = ProgramSet.zip(
    circuits=circuit_binding,
    observables=observables + [Y(0) @ Y(1)]
)
```

![\[ProgramSet.zip with CircuitBinding demonstrating five parallel executions using a shared circuit with individual observable per input set.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/program_set3.png)


Instead of `CircuitBinding()`, you can directly zip a list of observables with a list of circuits and input sets. 

```
program_set_4 = ProgramSet.zip(
    circuits=[circ1, circ2, circ3],
    input_sets=[{}, {}, {}],
    observables=observables[:3]
)
```

![\[ProgramSet.zip showing parallel execution of seven circuits with their corresponding individual input set and individual observables.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/program_set4.png)


For more information and examples on program sets, see the [Program set folder](https://github.com/amazon-braket/amazon-braket-examples/tree/main/examples/braket_features/program_sets) in the amazon-braket-examples Github.

**Inspect and run a program set on a device**

A program set's executable count equals its number of unique parameter-bound circuits. Calculate the total number of circuit executables and shots using the following code example.

```
# Number of shots per executable
shots = 10
num_executables = program_set_1.total_executables

# Calculate total number of shots across all executables
total_num_shots = shots*num_executables
```

**Note**  
With program sets, you pay a single per-task fee and a per-shot fee based on the total number of shots across all circuits in a program set.

To run the program set, use the following code example.

```
# Run the program set
task = device.run(
   program_set_1, shots=total_num_shots,
)
```

When using Rigetti devices, your program set may remain in the `RUNNING` state while tasks are partially finished and partially queued. For faster results, consider submitting your program set as a [Hybrid Job](braket-jobs-first.md).

**Analyzing results**

Run the following code to analyze and measure the results of the executables in a `ProgramSet`.

```
# Get the results from a program set 
result = task.result()

# Get the first executbable
first_program = result[0] 
first_executable = first_program[0]

# Inspect the results of the first executable
measurements_from_first_executable = first_executable.measurements
print(measurements_from_first_executable)
```

## Partial measurement
<a name="braket-partial-measurement"></a>

Instead of measuring all qubits in a quantum circuit, use partial measurement to measure individual qubits or a subset of qubits. 

**Note**  
Additional features such as mid-circuit measurement and feed-forward operations are available as Experimental Capabilities, see [Access to dynamic circuits on IQM devices](braket-experimental-capabilities.md#braket-access-dynamic-circuits).

**Example: Measure a subset of qubits**

The following code example demonstrates partial measurement by measuring only qubit 0 in a Bell state circuit.

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

# Use the local state vector simulator
device = LocalSimulator()

# Define an example bell circuit and measure qubit 0
circuit = Circuit().h(0).cnot(0, 1).measure(0)

# Run the circuit
task = device.run(circuit, shots=10)

# Get the results
result = task.result()

# Print the circuit and measured qubits
print(circuit)
print()
print("Measured qubits: ", result.measured_qubits)
```

## Manual qubit allocation
<a name="manual-qubit-allocation"></a>

When you run a quantum circuit on quantum computers from Rigetti, you can optionally use manual qubit allocation to control which qubits are used for your algorithm. The [Amazon Braket Console](https://console.aws.amazon.com/braket/home) and the [Amazon Braket SDK](https://github.com/aws/amazon-braket-sdk-python) help you to inspect the most recent calibration data of your selected quantum processing unit (QPU) device, so you can select the best qubits for your experiment.

Manual qubit allocation enables you to run circuits with greater accuracy and to investigate individual qubit properties. Researchers and advanced users optimize their circuit design based on the latest device calibration data and can obtain more accurate results.

The following example demonstrates how to allocate qubits explicitly.

```
# Import the device module
from braket.aws import AwsDevice

device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3")
circ = Circuit().h(0).cnot(0, 7)  # Indices of actual qubits in the QPU

# Set up S3 bucket (where results are stored)
my_bucket = "amazon-braket-s3-demo-bucket" # The name of the bucket
my_prefix = "your-folder-name" # The name of the folder in the bucket
s3_location = (my_bucket, my_prefix)

my_task = device.run(circ, s3_location, shots=100, disable_qubit_rewiring=True)
```

For more information, see [the Amazon Braket examples on GitHub](https://github.com/aws/amazon-braket-examples), or more specifically, this notebook: [Allocating Qubits on QPU Devices](https://github.com/aws/amazon-braket-examples/blob/main/examples/braket_features/Allocating_Qubits_on_QPU_Devices.ipynb).

## Verbatim compilation
<a name="verbatim-compilation"></a>

When you run a quantum circuit on gate-based quantum computers you can direct the compiler to run your circuits exactly as defined without any modifications. Using verbatim compilation, you can specify either that an entire circuit be preserved precisely as specified or that only specific parts of it be preserved (supported by Rigetti only). When developing algorithms for hardware benchmarking or error mitigation protocols, you need have the option to exactly specify the gates and circuit layouts that are running on the hardware. Verbatim compilation gives you direct control over the compilation process by turning off certain optimization steps, thereby ensuring that your circuits run exactly as designed.

Verbatim compilation is supported on the AQT, IonQ, IQM, and Rigetti devices and requires the use of native gates. When using verbatim compilation, it is advisable to check the topology of the device to ensure that gates are called on connected qubits and that the circuit uses the native gates supported on the hardware. The following example shows how to programmatically access the list of native gates supported by a device.

```
device.properties.paradigm.nativeGateSet
```

For Rigetti, qubit rewiring must be turned off by setting `disableQubitRewiring=True` for use with verbatim compilation. If `disableQubitRewiring=False` is set when using verbatim boxes in a compilation, the quantum circuit fails validation and does not run.

If verbatim compilation is enabled for a circuit and run on a QPU that does not support it, an error is generated indicating that an unsupported operation has caused the task to fail. As more quantum hardware natively support compiler functions, this feature will be expanded to include these devices. Devices that support verbatim compilation include it as a supported operation when queried with the following code.

```
from braket.aws import AwsDevice
from braket.device_schema.device_action_properties import DeviceActionType
device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3")
device.properties.action[DeviceActionType.OPENQASM].supportedPragmas
```

There is no additional cost associated with using verbatim compilation. You continue to be charged for quantum tasks executed on Braket QPU devices, notebook instances, and on-demand simulators based on current rates as specified on the [Amazon Braket Pricing](https://aws.amazon.com/braket/pricing/) page. For more information, see the [Verbatim compilation](https://github.com/aws/amazon-braket-examples/blob/main/examples/braket_features/Verbatim_Compilation.ipynb) example notebook.

**Note**  
If you are using OpenQASM to write your circuits for the AQT and IonQ devices, and you wish to map your circuit directly to the physical qubits, you need to use the `#pragma braket verbatim` as the `disableQubitRewiring` flag is ignored by OpenQASM.

## Noise simulation
<a name="noise-simulation"></a>

To instantiate the local noise simulator you can change the backend as follows.

```
# Import the device module
from braket.aws import AwsDevice

device = LocalSimulator(backend="braket_dm")
```

You can build noisy circuits in two ways:

1. Build the noisy circuit from the bottom up.

1. Take an existing, noise-free circuit and inject noise throughout.

The following example shows the approaches using a basic circuit with depolarizing noise and a custom Kraus channel.

```
import scipy.stats
import numpy as np

# Bottom up approach
# Apply depolarizing noise to qubit 0 with probability of 0.1
circ = Circuit().x(0).x(1).depolarizing(0, probability=0.1)

# Create an arbitrary 2-qubit Kraus channel
E0 = scipy.stats.unitary_group.rvs(4) * np.sqrt(0.8)
E1 = scipy.stats.unitary_group.rvs(4) * np.sqrt(0.2)
K = [E0, E1]

# Apply a two-qubit Kraus channel to qubits 0 and 2
circ = circ.kraus([0, 2], K)
```

```
from braket.circuits import Noise

# Inject noise approach
# Define phase damping noise
noise = Noise.PhaseDamping(gamma=0.1)
# The noise channel is applied to all the X gates in the circuit
circ = Circuit().x(0).y(1).cnot(0, 2).x(1).z(2)
circ_noise = circ.copy()
circ_noise.apply_gate_noise(noise, target_gates=Gate.X)
```

Running a circuit is the same user experience as before, as shown in the following two examples.

 **Example 1** 

```
task = device.run(circ, shots=100)
```

Or

 **Example 2** 

```
task = device.run(circ_noise, shots=100)
```

For more examples, see [the Braket introductory noise simulator example](https://github.com/aws/amazon-braket-examples/blob/main/examples/braket_features/Simulating_Noise_On_Amazon_Braket.ipynb) 

# Inspecting the circuit
<a name="braket-inspecting-circut"></a>

Quantum circuits in Amazon Braket have a pseudo-time concept called `Moments`. Each qubit can experience a single gate per `Moment`. The purpose of `Moments` is to make circuits and their gates easier to address and to provide a temporal structure.

**Note**  
Moments generally do not correspond to the real time at which gates are executed on a QPU.

The depth of a circuit is given by the total number of Moments in that circuit. You can view the circuit depth calling the method `circuit.depth` as shown in the following example.

```
from braket.circuits import Circuit

# Define a circuit with parametrized gates
circ = Circuit().rx(0, 0.15).ry(1, 0.2).cnot(0, 2).zz(1, 3, 0.15).x(0)
print(circ)
print('Total circuit depth:', circ.depth)
```

```
T  : │     0      │        1         │  2  │
      ┌──────────┐                    ┌───┐ 
q0 : ─┤ Rx(0.15) ├───●────────────────┤ X ├─
      └──────────┘   │                └───┘ 
      ┌──────────┐   │   ┌──────────┐       
q1 : ─┤ Ry(0.20) ├───┼───┤ ZZ(0.15) ├───────
      └──────────┘   │   └────┬─────┘       
                   ┌─┴─┐      │             
q2 : ──────────────┤ X ├──────┼─────────────
                   └───┘      │             
                         ┌────┴─────┐       
q3 : ────────────────────┤ ZZ(0.15) ├───────
                         └──────────┘       
T  : │     0      │        1         │  2  │
Total circuit depth: 3
```

The total circuit depth of the circuit above is 3 (shown as moments `0`, `1`, and `2`). You can check the gate operation for each moment.

 `Moments` functions as a dictionary of *key-value* pairs.
+ The key is `MomentsKey()`, which contains pseudo-time and qubit information.
+ The value is assigned in the type of `Instructions()`.

```
moments = circ.moments
for key, value in moments.items():
    print(key)
    print(value, "\n")
```

```
MomentsKey(time=0, qubits=QubitSet([Qubit(0)]), moment_type=<MomentType.GATE: 'gate'>, noise_index=0, subindex=0)
Instruction('operator': Rx('angle': 0.15, 'qubit_count': 1), 'target': QubitSet([Qubit(0)]), 'control': QubitSet([]), 'control_state': (), 'power': 1) 

MomentsKey(time=0, qubits=QubitSet([Qubit(1)]), moment_type=<MomentType.GATE: 'gate'>, noise_index=0, subindex=0)
Instruction('operator': Ry('angle': 0.2, 'qubit_count': 1), 'target': QubitSet([Qubit(1)]), 'control': QubitSet([]), 'control_state': (), 'power': 1) 

MomentsKey(time=1, qubits=QubitSet([Qubit(0), Qubit(2)]), moment_type=<MomentType.GATE: 'gate'>, noise_index=0, subindex=0)
Instruction('operator': CNot('qubit_count': 2), 'target': QubitSet([Qubit(0), Qubit(2)]), 'control': QubitSet([]), 'control_state': (), 'power': 1) 

MomentsKey(time=1, qubits=QubitSet([Qubit(1), Qubit(3)]), moment_type=<MomentType.GATE: 'gate'>, noise_index=0, subindex=0)
Instruction('operator': ZZ('angle': 0.15, 'qubit_count': 2), 'target': QubitSet([Qubit(1), Qubit(3)]), 'control': QubitSet([]), 'control_state': (), 'power': 1) 

MomentsKey(time=2, qubits=QubitSet([Qubit(0)]), moment_type=<MomentType.GATE: 'gate'>, noise_index=0, subindex=0)
Instruction('operator': X('qubit_count': 1), 'target': QubitSet([Qubit(0)]), 'control': QubitSet([]), 'control_state': (), 'power': 1)
```

You can also add gates to a circuit through `Moments`.

```
from braket.circuits import Instruction, Gate

new_circ = Circuit()
instructions = [Instruction(Gate.S(), 0),
                Instruction(Gate.CZ(), [1, 0]),
                Instruction(Gate.H(), 1)
                ]

new_circ.moments.add(instructions)
print(new_circ)
```

```
T  : │  0  │  1  │  2  │
      ┌───┐ ┌───┐       
q0 : ─┤ S ├─┤ Z ├───────
      └───┘ └─┬─┘       
              │   ┌───┐ 
q1 : ─────────●───┤ H ├─
                  └───┘ 
T  : │  0  │  1  │  2  │
```

# List of result types
<a name="braket-result-types"></a>

Amazon Braket can return different types of results when a circuit is measured using `ResultType`. A circuit can return the following types of results.
+  `AdjointGradient` returns the gradient (vector derivative) of the expectation value of a provided observable. This observable is acting on a provided target with respect to specified parameters using the adjoint differentiation method. You can only use this method when shots=0.
+  `Amplitude` returns the amplitude of specified quantum states in the output wave function. It is available on the SV1 and local simulators only.
+  `Expectation` returns the expectation value of a given observable, which can be specified with the `Observable` class introduced later in this chapter. The target qubits used to measure the observable must be specified, and the number of specified targets must equal the number of qubits on which the observable acts. If no targets are specified, the observable must operate only on 1 qubit and it is applied to all qubits in parallel.
+  `Probability` returns the probabilities of measuring computational basis states. If no targets are specified, `Probability` returns the probability of measuring all basis states. If targets are specified, only the marginal probabilities of the basis vectors on the specified qubits are returned. Managed simulators and QPUs are limited to 15 qubits maximum, and local simulators are limited to the system's memory size.
+  `Reduced density matrix` returns a density matrix for a subsystem of specified target qubits from a system of qubits. To limit the size of this result type, Braket limits the number of target qubits to a maximum of 8.
+  `StateVector` returns the full state vector. It is available on the local simulator.
+  `Sample` returns the measurement counts of a specified target qubit set and observable. If no targets are specified, the observable must operate only on 1 qubit and it is applied to all qubits in parallel. If targets are specified, the number of specified targets must equal the number of qubits on which the observable acts.
+  `Variance` returns the variance (`mean([x-mean(x)]2)`) of the specified target qubit set and observable as the requested result type. If no targets are specified, the observable must operate only on 1 qubit and it is applied to all qubits in parallel. Otherwise, the number of targets specified must equal the number of qubits to which the observable can be applied.

 **The supported result types for different providers:** 


|  |  |  |  |  |  |  |  |  | 
| --- |--- |--- |--- |--- |--- |--- |--- |--- |
|  |  Local sim  |   SV1   |   DM1   |   TN1   |   AQT   |   IonQ   |   IQM   |   Rigetti   | 
|  Adjoint gradient  |  N  |  Y  |  N  |  N  |  N  |  N  |  N  |  N  | 
|  Amplitude  |  Y  |  Y  |  N  |  N  |  N  |  N  |  N  |  N  | 
|  Expectation  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  | 
|  Probability  |  Y  |  Y  |  Y  |  N  |  Y  |  Y  |  Y  |  Y  | 
|  Reduced density matrix  |  Y  |  N  |  Y  |  N  |  N  |  N  |  N  |  N  | 
|  State vector  |  Y  |  N  |  N  |  N  |  N  |  N  |  N  |  N  | 
|  Sample  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  | 
|  Variance  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  | 

You can check the supported result types by examining the device properties, as shown in the following example.

```
from braket.aws import AwsDevice

device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3")

# Print the result types supported by this device
for iter in device.properties.action['braket.ir.openqasm.program'].supportedResultTypes:
    print(iter)
```

```
name='Sample' observables=['x', 'y', 'z', 'h', 'i'] minShots=10 maxShots=50000
name='Expectation' observables=['x', 'y', 'z', 'h', 'i'] minShots=10 maxShots=50000
name='Variance' observables=['x', 'y', 'z', 'h', 'i'] minShots=10 maxShots=50000
name='Probability' observables=None minShots=10 maxShots=50000
```

To call a `ResultType`, append it to a circuit, as shown in the following example.

```
from braket.circuits import Circuit, Observable

circ = Circuit().h(0).cnot(0, 1).amplitude(state=["01", "10"])
circ.probability(target=[0, 1])
circ.probability(target=0)
circ.expectation(observable=Observable.Z(), target=0)
circ.sample(observable=Observable.X(), target=0)
circ.state_vector()
circ.variance(observable=Observable.Z(), target=0)

# Print one of the result types assigned to the circuit
print(circ.result_types[0])
```

**Note**  
Different quantum devices provide results in various formats. For example, Rigetti devices return measurements, while IonQ devices provide probabilities. The Amazon Braket SDK offers a measurements property for all results. However, for devices that return probabilities, these measurements are post-computed and based on the probabilities, as per-shot measurements are not available. To determine if a result has been post-computed, check the `measurements_copied_from_device` on the result object. This operation is detailed in the [gate\$1model\$1quantum\$1task\$1result.py](https://github.com/aws/amazon-braket-sdk-python/blob/ca5b08dada4839ca31c012ff50aa20b656fd1879/src/braket/tasks/gate_model_quantum_task_result.py#L70-L72) file in the Amazon Braket SDK GitHub repository.

## Observables
<a name="braket-result-types-observables"></a>

Amazon Braket's `Observable` class allows you to measure a specific observable. 

You can apply only one unique non-identity observable to each qubit. An error occurs If you specify two or more different non-identity observables to the same qubit. For this purpose, each factor of a tensor product counts as an individual observable. This means you can have multiple tensor products on the same qubit, as long as the factors acting on that qubit remain the same.

An observable can be scaled and add other observables (scaled or not). This creates a `Sum` which can be used in the `AdjointGradient` result type.

The `Observable` class includes the following observables.

```
import numpy as np

Observable.I()
Observable.H()
Observable.X()
Observable.Y()
Observable.Z()

# Get the eigenvalues of the observable
print("Eigenvalue:", Observable.H().eigenvalues)
# Or rotate the basis to be computational basis
print("Basis rotation gates:", Observable.H().basis_rotation_gates)

# Get the tensor product of the observable for the multi-qubit case
tensor_product = Observable.Y() @ Observable.Z()
# View the matrix form of an observable by using
print("The matrix form of the observable:\n", Observable.Z().to_matrix())
print("The matrix form of the tensor product:\n", tensor_product.to_matrix())

# Factorize an observable in the tensor form
print("Factorize an observable:", tensor_product.factors)

# Self-define observables, given it is a Hermitian
print("Self-defined Hermitian:", Observable.Hermitian(matrix=np.array([[0, 1], [1, 0]])))

print("Sum of other (scaled) observables:", 2.0 * Observable.X() @ Observable.X() + 4.0 * Observable.Z() @ Observable.Z())
```

```
Eigenvalue: [ 1. -1.]
Basis rotation gates: (Ry('angle': -0.7853981633974483, 'qubit_count': 1),)
The matrix form of the observable:
 [[ 1.+0.j  0.+0.j]
 [ 0.+0.j -1.+0.j]]
The matrix form of the tensor product:
 [[ 0.+0.j  0.+0.j  0.-1.j  0.+0.j]
 [ 0.+0.j -0.+0.j  0.+0.j  0.+1.j]
 [ 0.+1.j  0.+0.j  0.+0.j  0.+0.j]
 [ 0.+0.j  0.-1.j  0.+0.j -0.+0.j]]
Factorize an observable: (Y('qubit_count': 1), Z('qubit_count': 1))
Self-defined Hermitian: Hermitian('qubit_count': 1, 'matrix': [[0.+0.j 1.+0.j], [1.+0.j 0.+0.j]])
Sum of other (scaled) observables: Sum(TensorProduct(X('qubit_count': 1), X('qubit_count': 1)), TensorProduct(Z('qubit_count': 1), Z('qubit_count': 1)))
```

## Parameters
<a name="braket-result-types-parameters"></a>

Circuits can incorporate free parameters. These free parameters only need to be constructed once to run multiple times, and can be used to compute gradients. 

Each free parameter uses a string-encoded name that is used to: 
+ Set parameter values
+ Identify which parameters to use

```
from braket.circuits import Circuit, FreeParameter, observables
from braket.parametric import FreeParameter

theta = FreeParameter("theta")
phi = FreeParameter("phi")
circ = Circuit().h(0).rx(0, phi).ry(0, phi).cnot(0, 1).xx(0, 1, theta)
```

## Adjoint gradient
<a name="braket-result-types-adjoint-gradient"></a>

The SV1 device calculates the adjoint gradient of an observable expectation value, including multi-term Hamiltonian. To differentiate parameters, specify their name (in string format) or by direct reference.

```
from braket.aws import AwsDevice
from braket.devices import Devices

device = AwsDevice(Devices.Amazon.SV1)

circ.adjoint_gradient(observable=3 * Observable.Z(0) @ Observable.Z(1) - 0.5 * observables.X(0), parameters = ["phi", theta])
```

Passing fixed parameter values as arguments to a parameterized circuit will remove the free parameters. Running this circuit with `AdjointGradient` produces an error, because the free parameters no longer exist. The follow code example demonstrates the correct and incorrect usage:

```
# Will error, as no free parameters will be present
#device.run(circ(0.2), shots=0)

# Will succeed
device.run(circ, shots=0, inputs={'phi': 0.2, 'theta': 0.2})
```

# Getting Expert advice
<a name="braket-expert-advice"></a>

Connect with quantum computing experts directly in the Braket management console to get additional guidance around your workloads. 

To explore Expert advice options through Braket Direct, open the Braket console, choose **Braket Direct** in the left pane, and navigate to the **Expert advice** section. The following Expert advice options are available:
+ **Braket office hours:** Braket office hours are 1:1 sessions, first come first-serve, and take place every month. Each available office hour slot is 30 minutes and free of charge. Talking to Braket experts can help you get from ideation to execution faster by exploring use-case-to-device fit, identifying options to best use Braket for your algorithm, and getting recommendations for how to use certain Braket features like Amazon Braket Hybrid Jobs, Braket Pulse, or Analog Hamiltonian Simulation.
  + To sign up for Braket office hours, select **Sign up** and fill out contact information, workload details, and your desired discussion topics.
  + You will receive a calendar invitation to the next available slot through your email.
**Note**  
For emergent issues or quick troubleshooting questions, we recommend reaching out to [AWS Support](https://console.aws.amazon.com/support/home#/case/create?issueType=technical). For non-urgent questions, you can also use the [AWS re:Post forum](https://repost.aws/tags/questions/TAhMWeHkpfSMSCxIFNqcqYog?view=all) or the [Quantum Computing Stack Exchange](https://quantumcomputing.stackexchange.com/questions/ask), where you can browse previously answered questions and ask new ones.
+ **Quantum hardware provider offerings:** IonQ, QuEra, and Rigetti each provide professional services offerings through AWS Marketplace.
  + To explore their offerings, select **Connect** and browse their listings.
  + To learn more about professional services offerings on the AWS Marketplace, see [ Professional services products](https://docs.aws.amazon.com/marketplace/latest/buyerguide/buyer-proserv-products.html).
+ **Amazon Quantum Solutions Lab (QSL):** The QSL is a collaborative research and professional services team staffed with quantum computing experts who can help you effectively explore quantum computing and assess the current performance of this technology.
  + To contact the QSL, select **Connect**, and fill out contact information and use case details.
  + The QSL team will reach out to you through email with next steps.

# Run your circuits with OpenQASM 3.0
<a name="braket-openqasm"></a>

 Amazon Braket now supports [OpenQASM 3.0](https://openqasm.com/) for gate-based quantum devices and simulators. This user guide provides information about the subset of OpenQASM 3.0 supported by Braket. Braket customers now have the choice of submitting Braket circuits with the [SDK](braket-constructing-circuit.md) or by directly providing OpenQASM 3.0 strings to all gate-based devices with the [Amazon Braket API](https://docs.aws.amazon.com/braket/latest/APIReference/Welcome.html) and the [Amazon Braket Python SDK](https://github.com/aws/amazon-braket-sdk-python).

The topics in this guide walk you through various examples of how to complete the following quantum tasks.
+  [Create and submit OpenQASM quantum tasks on different Braket devices](braket-openqasm-create-submit-task.md) 
+  [Access the supported operations and result types](braket-openqasm-device-support.md#braket-openqasm-supported-operations-results-result-types) 
+  [Simulate noise with OpenQASM](braket-openqasm-noise-simulation.md) 
+  [Use verbatim compilation with OpenQASM](braket-openqasm-verbatim-compilation.md) 
+  [Troubleshoot OpenQASM issues](https://docs.aws.amazon.com/braket/latest/developerguide/braket-troubleshooting-openqasm.html) 

This guide also provides an introduction to certain hardware-specific features that can be implemented with OpenQASM 3.0 on Braket and links to further resources.

**Topics**
+ [What is OpenQASM 3.0?](#braket-openqasm-what-is)
+ [When to use OpenQASM 3.0](#braket-openqasm-when-to-use)
+ [How OpenQASM 3.0 works](#braket-openqasm-how-it-works)
+ [Prerequisites](#braket-openqasm-prerequisites)
+ [What OpenQASM features does Braket support?](braket-openqasm-supported-features.md)
+ [Create and submit an example OpenQASM 3.0 quantum task](braket-openqasm-create-submit-task.md)
+ [Support for OpenQASM on different Braket devices](braket-openqasm-device-support.md)
+ [Simulate noise with OpenQASM 3.0](braket-openqasm-noise-simulation.md)
+ [Qubit rewiring with OpenQASM 3.0](braket-openqasm-rewire-qubits.md)
+ [Verbatim compilation with OpenQASM 3.0](braket-openqasm-verbatim-compilation.md)
+ [The Braket console](#braket-openqasm-braket-console)
+ [Additional resources](#braket-openqasm-more-resources)
+ [Computing gradients with OpenQASM 3.0](braket-openqasm-computing-gradients.md)
+ [Measuring specific qubits with OpenQASM 3.0](braket-openqasm-measure-qubits.md)

## What is OpenQASM 3.0?
<a name="braket-openqasm-what-is"></a>

The Open Quantum Assembly Language (OpenQASM) is an [intermediate representation](https://en.wikipedia.org/wiki/Intermediate_representation) for quantum instructions. OpenQASM is an open-source framework and is widely used for the specification of quantum programs for gate-based devices. With OpenQASM, users can program the quantum gates and measurement operations that form the building blocks of quantum computation. The previous version of OpenQASM (2.0) was used by a number of quantum programming libraries to describe basic programs.

The new version of OpenQASM (3.0) extends the previous version to include more features, such as pulse-level control, gate timing, and classical control flow to bridge the gap between end-user interface and hardware description language. Details and specification on the current version 3.0 are available on the GitHub [OpenQASM 3.x Live Specification](https://github.com/openqasm/openqasm). OpenQASM's future development is governed by the OpenQASM 3.0 [Technical Steering Committee](https://aws.amazon.com/blogs/quantum-computing/aws-joins-the-openqasm-3-0-technical-steering-committee/), of which AWS is a member alongside IBM, Microsoft, and the University of Innsbruck.

## When to use OpenQASM 3.0
<a name="braket-openqasm-when-to-use"></a>

OpenQASM provides an expressive framework to specify quantum programs through low-level controls that are not architecture specific, making it well suited as a representation across multiple gate-based devices. The Braket support for OpenQASM furthers its adoption as a consistent approach to developing gate-based quantum algorithms, reducing the need for users to learn and maintain libraries in multiple frameworks.

If you have existing libraries of programs in OpenQASM 3.0, you can adapt them for use with Braket rather than completely rewriting these circuits. Researchers and developers should also benefit from an increasing number of available third-party libraries with support for algorithm development in OpenQASM.

## How OpenQASM 3.0 works
<a name="braket-openqasm-how-it-works"></a>

Support for OpenQASM 3.0 from Braket provides feature parity with the current Intermediate Representation. This means that anything you can do today on hardware devices and on-demand simulators with Braket, you can do with OpenQASM using the Braket API. You can run OpenQASM 3.0 programs by directly supplying OpenQASM strings to all gate-based devices in a manner that is similar to how circuits are currently supplied to devices on Braket. Braket users can also integrate third-party libraries that support OpenQASM 3.0. The rest of this guide details how to develop OpenQASM representations for use with Braket.

## Prerequisites
<a name="braket-openqasm-prerequisites"></a>

To use OpenQASM 3.0 on Amazon Braket, you must have version v1.8.0 of the [Amazon Braket Python Schemas](https://github.com/aws/amazon-braket-schemas-python) and v1.17.0 or higher of the [Amazon Braket Python SDK](https://github.com/aws/amazon-braket-sdk-python).

If you are a first time user of Amazon Braket, you need to enable Amazon Braket. For instructions, see [Enable Amazon Braket](https://docs.aws.amazon.com/braket/latest/developerguide/braket-enable-overview.html).

# What OpenQASM features does Braket support?
<a name="braket-openqasm-supported-features"></a>

The following section lists the OpenQASM 3.0 data types, statements, and pragma instructions supported by Braket.

**Topics**
+ [Supported OpenQASM data types](#braket-openqasm-supported-features-datatypes)
+ [Supported OpenQASM statements](#braket-openqasm-supported-features-statements)
+ [Braket OpenQASM pragmas](#braket-openqasm-supported-features-pragmas)
+ [Advanced feature support for OpenQASM on the Local Simulator](#braket-openqasm-supported-features-advanced-feature-local-simulator)
+ [Supported operations and grammar with OpenPulse](#braket-openpulse-supported-operations-grammar)

## Supported OpenQASM data types
<a name="braket-openqasm-supported-features-datatypes"></a>

The following OpenQASM data types are supported by Amazon Braket.
+ Non-negative integers are used for (virtual and physical) qubit indices:
  +  `cnot q[0], q[1];` 
  +  `h $0;` 
+ Floating-point numbers or constants may be used for gate rotation angles:
  +  `rx(-0.314) $0;` 
  +  `rx(pi/4) $0;` 

**Note**  
pi is a built-in constant in OpenQASM and cannot be used as a parameter name.
+ Arrays of complex numbers (with the OpenQASM `im` notation for imaginary part) are allowed in result type pragmas for defining general hermitian observables and in unitary pragmas:
  +  `#pragma braket unitary [[0, -1im], [1im, 0]] q[0]` 
  +  `#pragma braket result expectation hermitian([[0, -1im], [1im, 0]]) q[0]` 

## Supported OpenQASM statements
<a name="braket-openqasm-supported-features-statements"></a>

The following OpenQASM statements are supported by Amazon Braket.
+  `Header: OPENQASM 3;` 
+ Classic bit declarations:
  +  `bit b1;` (equivalently, `creg b1;`)
  +  `bit[10] b2;` (equivalently, `creg b2[10];`)
+ Qubit declarations:
  +  `qubit b1;` (equivalently, `qreg b1;`)
  +  `qubit[10] b2;` (equivalently, `qreg b2[10];`)
+ Indexing within arrays: `q[0]` 
+ Input: `input float alpha;` 
+ specification of physical qubits: `$0` 
+ Supported gates and operations on a device:
  +  `h $0;` 
  +  `iswap q[0], q[1];` 

**Note**  
A device's supported gates can be found in the device properties for OpenQASM actions; no gate definitions are needed to use these gates.
+ Verbatim box statements. Currently, we do not support box duration notation. Native gates and physical qubits are required in verbatim boxes.

```
#pragma braket verbatim
box{
    rx(0.314) $0;
}
```
+ Measurement and measurement assignment on qubits or a whole qubit register.
  +  `measure $0;` 
  +  `measure q;` 
  +  `measure q[0];` 
  +  `b = measure q;` 
  +  `measure q → b;` 
+ Barrier statements provide explicit control over circuit compilation and execution by preventing gate reordering and optimizations across barrier boundaries. They also enforce strict temporal ordering during execution, ensuring all operations before a barrier complete before subsequent operations begin.
  +  `barrier;` 
  +  `barrier q[0], q[1];` 
  +  `barrier $3, $6;` 

## Braket OpenQASM pragmas
<a name="braket-openqasm-supported-features-pragmas"></a>

The following OpenQASM pragma instructions are supported by Amazon Braket.
+ Noise pragmas
  +  `#pragma braket noise bit_flip(0.2) q[0]` 
  +  `#pragma braket noise phase_flip(0.1) q[0]` 
  +  `#pragma braket noise pauli_channel` 
+ Verbatim pragmas
  +  `#pragma braket verbatim` 
+ Result type pragmas
  + Basis invariant result types:
    + State vector: `#pragma braket result state_vector` 
    + Density matrix: `#pragma braket result density_matrix` 
  + Gradient computation pragmas:
    + Adjoint gradient: `#pragma braket result adjoint_gradient expectation(2.2 * x[0] @ x[1]) all` 
  + Z basis result types:
    + Amplitude: `#pragma braket result amplitude "01"` 
    + Probability: `#pragma braket result probability q[0], q[1]` 
  + Basis rotated result types
    + Expectation: `#pragma braket result expectation x(q[0]) @ y([q1])` 
    + Variance: `#pragma braket result variance hermitian([[0, -1im], [1im, 0]]) $0` 
    + Sample: `#pragma braket result sample h($1)` 

**Note**  
OpenQASM 3.0 is backwards compatible with OpenQASM 2.0, so programs written using 2.0 can run on Braket. However the features of OpenQASM 3.0 supported by Braket do have some minor syntax differences, such as `qreg` vs `creg` and `qubit` vs `bit`. There are also differences in measurement syntax, and these need to be supported with their correct syntax.

## Advanced feature support for OpenQASM on the Local Simulator
<a name="braket-openqasm-supported-features-advanced-feature-local-simulator"></a>

The `LocalSimulator` supports advanced OpenQASM features which are not offered as part of Braket's QPU's or on-demand simulators. The following list of features are only supported in the `LocalSimulator`:
+ Gate modifiers
+ OpenQASM built-in gates
+ Classical variables
+ Classical operations
+ Custom gates
+ Classical control
+ QASM files
+ Subroutines

For examples of each advanced feature, see this [sample notebook](https://github.com/aws/amazon-braket-examples/blob/main/examples/braket_features/Simulating_Advanced_OpenQASM_Programs_with_the_Local_Simulator.ipynb). For the full OpenQASM specification, see the [OpenQASM website](https://openqasm.com/language/index.html).

## Supported operations and grammar with OpenPulse
<a name="braket-openpulse-supported-operations-grammar"></a>

 **Supported OpenPulse Data Types** 

Cal blocks:

```
cal {
    ...
}
```

Defcal blocks:

```
// 1 qubit
defcal x $0 {
...
}

// 1 qubit w. input parameters as constants
defcal my_rx(pi) $0 {
...
}

// 1 qubit w. input parameters as free parameters
defcal my_rz(angle theta) $0 {
...
}

// 2 qubit (above gate args are also valid)
defcal cz $1, $0 {
...
}
```

Frames:

```
frame my_frame = newframe(port_0, 4.5e9, 0.0);
```

Waveforms:

```
// prebuilt
waveform my_waveform_1 = constant(1e-6, 1.0);

//arbitrary
waveform my_waveform_2 = {0.1 + 0.1im, 0.1 + 0.1im, 0.1, 0.1};
```

 **Custom Gate Calibration Example:** 

```
cal {
    waveform wf1 = constant(1e-6, 0.25);
}

defcal my_x $0 {
   play(wf1, q0_rf_frame);
}

defcal my_cz $1, $0 {
    barrier q0_q1_cz_frame, q0_rf_frame;
    play(q0_q1_cz_frame, wf1);
    delay[300ns] q0_rf_frame
    shift_phase(q0_rf_frame, 4.366186381749424);
    delay[300ns] q0_rf_frame;
    shift_phase(q0_rf_frame.phase, 5.916747563126659);
    barrier q0_q1_cz_frame, q0_rf_frame;
    shift_phase(q0_q1_cz_frame, 2.183093190874712);
}

bit[2] ro;
my_x $0;
my_cz $1,$0;
c[0] = measure $0;
```

 **Arbitrary pulse example:** 

```
bit[2] ro;
cal {
    waveform wf1 = {0.1 + 0.1im, 0.1 + 0.1im, 0.1, 0.1};
    barrier q0_drive, q0_q1_cross_resonance;
    play(q0_q1_cross_resonance, wf1);
    delay[300ns] q0_drive;
    shift_phase(q0_drive, 4.366186381749424);
    delay[300dt] q0_drive;
   barrier q0_drive, q0_q1_cross_resonance;
   play(q0_q1_cross_resonance, wf1);
    ro[0] = capture_v0(r0_measure);
    ro[1] = capture_v0(r1_measure);
}
```

# Create and submit an example OpenQASM 3.0 quantum task
<a name="braket-openqasm-create-submit-task"></a>

You can use the Amazon Braket Python SDK, Boto3, or the AWS CLI to submit OpenQASM 3.0 quantum tasks to an Amazon Braket device.

**Topics**
+ [An example OpenQASM 3.0 program](#braket-openqasm-example-program)
+ [Use the Python SDK to create OpenQASM 3.0 quantum tasks](#braket-openqasm-create-tasks-with-python-sdk)
+ [Use Boto3 to create OpenQASM 3.0 quantum tasks](#braket-openqasm-create-tasks-with-boto3)
+ [Use the AWS CLI to create OpenQASM 3.0 tasks](#braket-openqasm-create-tasks-with-aws-cli)

## An example OpenQASM 3.0 program
<a name="braket-openqasm-example-program"></a>

To create an OpenQASM 3.0 task, you can start with a basic OpenQASM 3.0 program (ghz.qasm) that prepares a [GHZ state](https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) as shown in the following example.

```
// ghz.qasm
// Prepare a GHZ state
OPENQASM 3;

qubit[3] q;
bit[3] c;

h q[0];
cnot q[0], q[1];
cnot q[1], q[2];

c = measure q;
```

## Use the Python SDK to create OpenQASM 3.0 quantum tasks
<a name="braket-openqasm-create-tasks-with-python-sdk"></a>

You can use the [Amazon Braket Python SDK](https://github.com/aws/amazon-braket-sdk-python) to submit this program to an Amazon Braket device with the following code. Be sure to replace the example Amazon S3 bucket location “amzn-s3-demo-bucket” with your own Amazon S3 bucket name.

```
with open("ghz.qasm", "r") as ghz:
    ghz_qasm_string = ghz.read()

# Import the device module
from braket.aws import AwsDevice
# Choose the Rigetti device
device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3")
from braket.ir.openqasm import Program

program = Program(source=ghz_qasm_string)
my_task = device.run(program)

# Specify an optional s3 bucket location and number of shots
s3_location = ("amzn-s3-demo-bucket", "openqasm-tasks")
my_task = device.run(
    program,
    s3_location,
    shots=100,
)
```

## Use Boto3 to create OpenQASM 3.0 quantum tasks
<a name="braket-openqasm-create-tasks-with-boto3"></a>

You can also use [AWS Python SDK for Braket (Boto3)](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/braket.html) to create the quantum tasks using OpenQASM 3.0 strings, as shown in the following example. The following code snippet references ghz.qasm that prepares a [GHZ state](https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) as shown above.

```
import boto3
import json

my_bucket = "amzn-s3-demo-bucket"
s3_prefix = "openqasm-tasks"

with open("ghz.qasm") as f:
    source = f.read()

action = {
    "braketSchemaHeader": {
        "name": "braket.ir.openqasm.program",
        "version": "1"
    },
    "source": source
}
device_parameters = {}
device_arn = "arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3"
shots = 100

braket_client = boto3.client('braket', region_name='us-west-1')
rsp = braket_client.create_quantum_task(
    action=json.dumps(
        action
    ),
    deviceParameters=json.dumps(
        device_parameters
    ),
    deviceArn=device_arn,
    shots=shots,
    outputS3Bucket=my_bucket,
    outputS3KeyPrefix=s3_prefix,
)
```

## Use the AWS CLI to create OpenQASM 3.0 tasks
<a name="braket-openqasm-create-tasks-with-aws-cli"></a>

The [AWS Command Line Interface (CLI)](https://aws.amazon.com/cli/) can also be used to submit OpenQASM 3.0 programs, as shown in the following example.

```
aws braket create-quantum-task \
    --region "us-west-1" \
    --device-arn "arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3" \
    --shots 100 \
    --output-s3-bucket "amzn-s3-demo-bucket" \
    --output-s3-key-prefix "openqasm-tasks" \
    --action '{
        "braketSchemaHeader": {
            "name": "braket.ir.openqasm.program",
            "version": "1"
        },
        "source": $(cat ghz.qasm)
    }'
```

# Support for OpenQASM on different Braket devices
<a name="braket-openqasm-device-support"></a>

For devices supporting OpenQASM 3.0, the `action` field supports a new action through the `GetDevice` response, as shown in the following example for the Rigetti and IonQ devices.

```
//OpenQASM as available with the Rigetti device capabilities
{
    "braketSchemaHeader": {
        "name": "braket.device_schema.rigetti.rigetti_device_capabilities",
        "version": "1"
    },
    "service": {...},
    "action": {
        "braket.ir.jaqcd.program": {...},
        "braket.ir.openqasm.program": {
            "actionType": "braket.ir.openqasm.program",
            "version": [
                "1"
            ],
            ….
        }
    }
}

//OpenQASM as available with the IonQ device capabilities
{
    "braketSchemaHeader": {
        "name": "braket.device_schema.ionq.ionq_device_capabilities",
        "version": "1"
    },
    "service": {...},
    "action": {
        "braket.ir.jaqcd.program": {...},
        "braket.ir.openqasm.program": {
            "actionType": "braket.ir.openqasm.program",
            "version": [
                "1"
            ],
            ….
        }
    }
}
```

For devices that support pulse control, the `pulse` field is displayed in the `GetDevice` response. The following example show this `pulse` field for the Rigetti device.

```
// Rigetti
{
  "pulse": {
    "braketSchemaHeader": {
      "name": "braket.device_schema.pulse.pulse_device_action_properties",
      "version": "1"
    },
    "supportedQhpTemplateWaveforms": {
      "constant": {
        "functionName": "constant",
        "arguments": [
          {
            "name": "length",
            "type": "float",
            "optional": false
          },
          {
            "name": "iq",
            "type": "complex",
            "optional": false
          }
        ]
      },
      ...
    },
    "ports": {
      "q0_ff": {
        "portId": "q0_ff",
        "direction": "tx",
        "portType": "ff",
        "dt": 1e-9,
        "centerFrequencies": [
          375000000
        ]
      },
      ...
    },
    "supportedFunctions": {
      "shift_phase": {
        "functionName": "shift_phase",
        "arguments": [
          {
            "name": "frame",
            "type": "frame",
            "optional": false
          },
          {
            "name": "phase",
            "type": "float",
            "optional": false
          }
        ]
      },
     ...
    },
    "frames": {
      "q0_q1_cphase_frame": {
        "frameId": "q0_q1_cphase_frame",
        "portId": "q0_ff",
        "frequency": 462475694.24460185,
        "centerFrequency": 375000000,
        "phase": 0,
        "associatedGate": "cphase",
        "qubitMappings": [
          0,
          1
        ]
      },
      ...
    },
    "supportsLocalPulseElements": false,
    "supportsDynamicFrames": false,
    "supportsNonNativeGatesWithPulses": false,
    "validationParameters": {
      "MAX_SCALE": 4,
      "MAX_AMPLITUDE": 1,
      "PERMITTED_FREQUENCY_DIFFERENCE": 400000000
    }
  }
}
```

The preceding fields detail the following:

 **Ports:** 

Describes pre-made external (`extern`) device ports declared on the QPU in addition to the associated properties of the given port. All ports listed in this structure are pre-declared as valid identifiers within the `OpenQASM 3.0` program submitted by the user. The additional properties for a port include:
+ Port id (portId)
  + The port name declared as an identifier in OpenQASM 3.0.
+ Direction (direction)
  + The direction of the port. Drive ports transmit pulses (direction “tx”), while measurement ports receive pulses (direction “rx”).
+ Port type (portType)
  + The type of action for which this port is responsible (for example, drive, capture, or ff - fast-flux).
+ Dt (dt)
  + The time in seconds that represents a single sample time step on the given port.
+ Qubit mappings (qubitMappings)
  + The qubits associated with the given port.
+ Center frequencies (centerFrequencies)
  + A list of the associated center frequencies for all pre-declared or user-defined frames on the port. For more information, refer to Frames.
+ QHP Specific Properties (qhpSpecificProperties)
  + An optional map detailing existing properties about the port specific to the QHP.

 **Frames:** 

Describes pre-made external frames declared on the QPU as well as associated properties about the frames. All frames listed in this structure are pre-declared as valid identifiers within the `OpenQASM 3.0` program submitted by the user. The additional properties for a frame include:
+ Frame Id (frameId)
  + The frame name declared as an identifier in OpenQASM 3.0.
+ Port Id (portId)
  + The associated hardware port for the frame.
+ Frequency (frequency)
  + The default initial frequency of the frame.
+ Center Frequency (centerFrequency)
  + The center of the frequency bandwidth for the frame. Typically, frames may only be adjusted to a certain bandwidth around the center frequency. As a result, frequency adjustments should stay within a given delta of the center frequency. You can find the bandwidth value in the validation parameters.
+ Phase (phase)
  + The default initial phase of the frame.
+ Associated Gate (associatedGate)
  + The gates associated with the given frame.
+ Qubit Mappings (qubitMappings)
  + The qubits associated with the given frame.
+ QHP Specific Properties (qhpSpecificProperties)
  + An optional map detailing existing properties about the frame specific to the QHP.

 **SupportsDynamicFrames:** 

Describes whether a frame can be declared in `cal` or `defcal` blocks through the OpenPulse `newframe` function. If this is false, only frames listed in the frame structure may be used within the program.

 **SupportedFunctions:** 

Describes the OpenPulse functions that are supported for the device in addition to the associated arguments, argument types, and return types for the given functions. To see examples of using the OpenPulse functions, see the [OpenPulse specification](https://openqasm.com/language/openpulse.html). At this time, Braket supports:
+ shift\$1phase
  + Shifts the phase of a frame by a specified value
+ set\$1phase
  + Sets the phase of frame to the specified value
+ swap\$1phases
  + Swaps the phases between two frames.
+ shift\$1frequency
  + Shifts the frequency of a frame by a specified value
+ set\$1frequency
  + Sets the frequency of frame to the specified value
+ play
  + Schedules a waveform
+ capture\$1v0
  + Returns the value on a capture frame to a bit register

 **SupportedQhpTemplateWaveforms:** 

Describes the pre-built waveform functions available on the device and the associated arguments and types. By default, Braket Pulse offers pre-built waveform routines on all devices, which are:

 ** *Constant* ** 

![\[Mathematical equation showing a constant function with parameters t, tau, and iq where the output is always equal to iq.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/ConstantFunction.png)


 `τ` is the length of the waveform and `iq` is a complex number.

```
def constant(length, iq)
```

 ** *Gaussian* ** 

![\[Mathematical equation showing the Gaussian function with parameters t, tau, sigma, A=1, and ZaE=0.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/GaussianFunction.png)


 `τ` is the length of the waveform, `σ` is the width of the Gaussian, and `A` is the amplitude. If setting `ZaE` to `True`, the Gaussian is offset and rescaled such that it is equal to zero at the start and end of the waveform, and reaches `A` at maximum.

```
def gaussian(length, sigma, amplitude=1, zero_at_edges=False)
```

 ** *DRAG Gaussian* ** 

![\[Mathematical equation for DRAG Gaussian distribution with parameters t, tau, sigma, beta, A=1, and ZaE=0.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/DRAGGaussianFunction.png)


 `τ` is the length of the waveform, `σ` is the width of the gaussian, `β` is a free parameter, and `A` is the amplitude. If setting `ZaE` to `True`, the Derivative Removal by Adiabatic Gate (DRAG) Gaussian is offset and rescaled such that it is equal to zero at the start and end of the waveform, and the real part reaches `A` at maximum. For more information about the DRAG waveform, see the paper [Simple Pulses for Elimination of Leakage in Weakly Nonlinear Qubits](https://doi.org/10.1103/PhysRevLett.103.110501).

```
def drag_gaussian(length, sigma, beta, amplitude=1, zero_at_edges=False)
```

 ** *Erf Square* ** 

![\[Mathematical equation for Erf Square distribution with parameters t, Length, Width, sigma, A=1, and ZaE=0.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/ErfSquareFunction.PNG)


Where `L` is the length, `W` is the width of the waveform, `σ` defines how fast the edges rise and fall, `t1​=(L−W)/2` and `t22=(L+W)/2`, `A` is the amplitude. If setting `ZaE` to `True`, the Gaussian is offset and rescaled such that it is equal to zero at the start and end of the waveform, and reaches `A` at maximum. The following equation is the rescaled version of the waveform.

![\[Mathematical equation for the rescaled Erf Square distribution with parameters ZaE=1.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/RescaledErfSquareFunction.PNG)


Where `a=erf(W/2σ)`and `b=erf(-t1​/σ)/2+erf(t2​/σ)/2`.

```
def erf_square(length, width, sigma, amplitude=1, zero_at_edges=False)
```

 **SupportsLocalPulseElements:** 

Describes whether pulse elements, such as ports, frames, and waveforms may be defined locally in `defcal` blocks. If the value is `false`, elements must be defined in `cal` blocks.

 **SupportsNonNativeGatesWithPulses:** 

Describes whether we can or cannot use non-native gates in combination with pulse programs. For example, you cannot use a non-native gate like an `H` gate in a program without first defining the gate through `defcal` for the used qubit. You can find the list of native gates `nativeGateSet` key under the device capabilities.

 **ValidationParameters:** 

Describes pulse element validation boundaries, including:
+ Maximum Scale / Maximum Amplitude values for waveforms (arbitrary and pre-built)
+ Maximum frequency bandwidth from supplied center frequency in Hz
+ Minimum pulse length/duration in seconds
+ Maximum pulse length/duration in seconds

## Supported Operations, Results and Result Types with OpenQASM
<a name="braket-openqasm-supported-operations-results-result-types"></a>

To find out which OpenQASM 3.0 features each device supports, you can refer to the `braket.ir.openqasm.program` key in the `action` field on the device capabilities output. For example, the following are the supported operations and result types available for the Braket State Vector simulator SV1.

```
...
  "action": {
    "braket.ir.jaqcd.program": {
      ...
    },
 "braket.ir.openqasm.program": {
      "version": [
        "1.0"
      ],
      "actionType": "braket.ir.openqasm.program",
      "supportedOperations": [
        "ccnot",
        "cnot",
        "cphaseshift",
        "cphaseshift00",
        "cphaseshift01",
        "cphaseshift10",
        "cswap",
        "cy",
        "cz",
        "h",
        "i",
        "iswap",
        "pswap",
        "phaseshift",
        "rx",
        "ry",
        "rz",
        "s",
        "si",
        "swap",
        "t",
        "ti",
        "v",
        "vi",
        "x",
        "xx",
        "xy",
        "y",
        "yy",
        "z",
        "zz"
      ],
      "supportedPragmas": [
        "braket_unitary_matrix"
      ],
      "forbiddenPragmas": [],
      "maximumQubitArrays": 1,
      "maximumClassicalArrays": 1,
      "forbiddenArrayOperations": [
        "concatenation",
        "negativeIndex",
        "range",
        "rangeWithStep",
        "slicing",
        "selection"
      ],
      "requiresAllQubitsMeasurement": true,
      "supportsPhysicalQubits": false,
      "requiresContiguousQubitIndices": true,
      "disabledQubitRewiringSupported": false,
      "supportedResultTypes": [
        {
          "name": "Sample",
          "observables": [
            "x",
            "y",
            "z",
            "h",
            "i",
            "hermitian"
          ],
          "minShots": 1,
          "maxShots": 100000
        },
        {
          "name": "Expectation",
          "observables": [
            "x",
            "y",
            "z",
            "h",
            "i",
            "hermitian"
          ],
          "minShots": 0,
          "maxShots": 100000
        },
        {
          "name": "Variance",
          "observables": [
            "x",
            "y",
            "z",
            "h",
            "i",
            "hermitian"
          ],
          "minShots": 0,
          "maxShots": 100000
        },
        {
          "name": "Probability",
          "minShots": 1,
          "maxShots": 100000
        },
        {
          "name": "Amplitude",
          "minShots": 0,
          "maxShots": 0
        }
        {
          "name": "AdjointGradient",
          "minShots": 0,
          "maxShots": 0
        }
      ]
    }
  },
...
```

# Simulate noise with OpenQASM 3.0
<a name="braket-openqasm-noise-simulation"></a>

To simulate noise with OpenQASM3, you use *pragma* instructions to add noise operators. For example, to simulate the noisy version of the [GHZ program](braket-openqasm-create-submit-task.md#braket-openqasm-example-program) provided previously, you can submit the following OpenQASM program.

```
// ghz.qasm
// Prepare a GHZ state
OPENQASM 3;

qubit[3] q;
bit[3] c;

h q[0];
#pragma braket noise depolarizing(0.75) q[0] cnot q[0], q[1];
#pragma braket noise depolarizing(0.75) q[0]
#pragma braket noise depolarizing(0.75) q[1] cnot q[1], q[2];
#pragma braket noise depolarizing(0.75) q[0]
#pragma braket noise depolarizing(0.75) q[1]

c = measure q;
```

Specifications for all supported pragma noise operators are provided in the following list.

```
#pragma braket noise bit_flip(<float in [0,1/2]>) <qubit>
#pragma braket noise phase_flip(<float in [0,1/2]>) <qubit>
#pragma braket noise pauli_channel(<float>, <float>, <float>)  <qubit>
#pragma braket noise depolarizing(<float in [0,3/4]>) <qubit>
#pragma braket noise two_qubit_depolarizing(<float in [0,15/16]>) <qubit>, <qubit>
#pragma braket noise two_qubit_dephasing(<float in [0,3/4]>) <qubit>, <qubit>
#pragma braket noise amplitude_damping(<float in [0,1]>) <qubit>
#pragma braket noise generalized_amplitude_damping(<float in [0,1]> <float in [0,1]>)  <qubit>
#pragma braket noise phase_damping(<float in [0,1]>) <qubit>
#pragma braket noise kraus([[<complex m0_00>, ], ...], [[<complex m1_00>, ], ...], ...) <qubit>[, <qubit>]     // maximum of 2 qubits and maximum of 4 matrices for 1 qubit, 16 for 2
```

## Kraus operator
<a name="braket-openqasm-kraus-operator"></a>

To generate a Kraus operator, you can iterate through a list of matrices, printing each element of the matrix as a complex expression.

When using Kraus operators, remember the following:
+ The number of qubits must not exceed 2. The [current definition in the schemas](https://github.com/aws/amazon-braket-sdk-python/blob/0d28a8fa89263daf5d88bc706e79200d8dc091a8/src/braket/circuits/noises.py#L811-L814)) sets this limit.
+ The length of the argument list must be a multiple of 8. This means it must be composed only of 2x2 matrices.
+ The total length does not exceed 22\$1num\$1qubits matrices. This means 4 matrices for 1 qubit and 16 for 2 qubits.
+ All supplied matrices are [completely positive trace preserving (CPTP)](https://github.com/aws/amazon-braket-sdk-python/blob/0d28a8fa89263daf5d88bc706e79200d8dc091a8/src/braket/circuits/quantum_operator_helpers.py#L94-L108).
+ The product of the Kraus operators with their transpose conjugates need to add up to an identity matrix.

# Qubit rewiring with OpenQASM 3.0
<a name="braket-openqasm-rewire-qubits"></a>

Amazon Braket supports the physical qubit notation within OpenQASM on Rigetti devices (to learn more see this [page](https://github.com/openqasm/openqasm/blob/main/source/language/types.rst)). When using physical qubits with the [naive rewiring strategy](https://pyquil-docs.rigetti.com/en/v2.28.1/compiler.html#naive), ensure that the qubits are connected on the selected device. Alternatively, if qubit registers are used instead, the PARTIAL rewiring strategy is enabled by default on Rigetti devices.

```
// ghz.qasm
// Prepare a GHZ state
OPENQASM 3;

h $0;
cnot $0, $1;
cnot $1, $2;

measure $0;
measure $1;
measure $2;
```

# Verbatim compilation with OpenQASM 3.0
<a name="braket-openqasm-verbatim-compilation"></a>

When you run a quantum circuit on quantum computers provided by vendors such as Rigetti, and IonQ, you can direct the compiler to run your circuits exactly as defined, without any modifications. This feature is known as *verbatim compilation*. With Rigetti devices, you can specify precisely what gets preserved-either an entire circuit or only specific parts of it. To preserve only specific parts of a circuit, you will need to use native gates within the preserved regions. Currently, IonQ only supports verbatim compilation for the entire circuit, so every instruction in the circuit needs to be enclosed in a verbatim box.

With OpenQASM, you can explicitly specify a verbatim pragma around a box of code that is then left untouched and not optimized by the low-level compilation routine of the hardware. The following code example shows how to use the `#pragma braket verbatim` directive to achieve this.

```
OPENQASM 3;

bit[2] c;

#pragma braket verbatim
box{
    rx(0.314159) $0;
    rz(0.628318) $0, $1;
    cz $0, $1;
}

c[0] = measure $0;
c[1] = measure $1;
```

For more detailed information on the process of verbatim compilation, including examples and best practices, see the [Verbatim compilation](https://github.com/aws/amazon-braket-examples/blob/main/examples/braket_features/Verbatim_Compilation.ipynb) sample notebook available in the amazon-braket-examples github repository.

## The Braket console
<a name="braket-openqasm-braket-console"></a>

OpenQASM 3.0 tasks are available and can be managed within the Amazon Braket console. On the console, you have the same experience submitting quantum tasks in OpenQASM 3.0 as you had submitting existing quantum tasks.

## Additional resources
<a name="braket-openqasm-more-resources"></a>

OpenQASM is available in all Amazon Braket Regions.

For an example notebook for getting started with OpenQASM on Amazon Braket, see [Braket Tutorials GitHub](https://github.com/aws/amazon-braket-examples/blob/main/examples/braket_features/Getting_Started_with_OpenQASM_on_Braket.ipynb).

# Computing gradients with OpenQASM 3.0
<a name="braket-openqasm-computing-gradients"></a>

Amazon Braket supports the computation of gradients on both on-demand and local simulators when running in the `shots=0` (exact) mode. This is achieved through the use of the adjoint differentiation method. To specify the gradient you want to compute, you can provide the appropriate pragma, as demonstrated in code in the following example.

```
OPENQASM 3.0;
input float alpha;

bit[2] b;
qubit[2] q;

h q[0];
h q[1];
rx(alpha) q[0];
rx(alpha) q[1];
b[0] = measure q[0];
b[1] = measure q[1];

#pragma braket result adjoint_gradient h(q[0]) @ i(q[1]) alpha
```

Instead of listing all the individual parameters explicitly, you can also specify the `all` keyword within the pragma. This will compute the gradient with respect to all of the `input` parameters listed, which can be a convenient option when the number of parameters is very large. In this case, the pragma will look like the code in the following example.

```
#pragma braket result adjoint_gradient h(q[0]) @ i(q[1]) all
```

All observable types are supported in Amazon Braket's OpenQASM 3.0 implementation, including individual operators, tensor products, Hermitian observables, and `Sum` observables. The specific operator you want to use when computing gradients must be wrapped within the `expectation()` function, and the qubits that each term of the observable acts upon must be explicitly specified.

# Measuring specific qubits with OpenQASM 3.0
<a name="braket-openqasm-measure-qubits"></a>

The local state vector simulator and local density matrix simulator provided by Amazon Braket support the submission of OpenQASM programs where a subset of the circuit's qubits can be selectively measured. This capability, often referred to as partial measurement, allows for more targeted and efficient quantum computations. For example, in the following code snippet, you can create a two-qubit circuit and choose to only measure the first qubit, while leaving the second qubit unmeasured.

```
partial_measure_qasm = """
OPENQASM 3.0;
bit[1] b;
qubit[2] q;
h q[0];
cnot q[0], q[1];
b[0] = measure q[0];
"""
```

In this example, we have a quantum circuit with two qubits, `q[0]` and `q[1]`, but we are only interested in measuring the state of the first qubit. This is achieved by the line `b[0] = measure q[0]`, which measures the state of qubit[0] and stores the result in the classical bit b[0]. To run this partial measurement scenario, we can run the following code on the local state vector simulator provided by Amazon Braket.

```
from braket.devices import LocalSimulator

local_sim = LocalSimulator()
partial_measure_local_sim_task = local_sim.run(OpenQASMProgram(source=partial_measure_qasm), shots = 10)
partial_measure_local_sim_result = partial_measure_local_sim_task.result()
print(partial_measure_local_sim_result.measurement_counts)
print("Measured qubits: ", partial_measure_local_sim_result.measured_qubits)
```

You can check whether a device supports partial measurement by inspecting the `requiresAllQubitsMeasurement` field in its action properties; if it is `False`, then partial measurement is supported.

```
from braket.devices import Devices
            
AwsDevice(Devices.Rigetti.Ankaa3).properties.action['braket.ir.openqasm.program'].requiresAllQubitsMeasurement
```

Here, `requiresAllQubitsMeasurement` is `False`, which indicates that not all qubits must be measured.

# Explore Experimental Capabilities
<a name="braket-experimental-capabilities"></a>

Experimental capabilities provide access to hardware with limited availability and emergent new software features. These features may impact device performance beyond standard specifications. You can automatically enable experimental software capabilities on a per-task basis through the Amazon Braket SDK.

To use experimental capabilities, specify the `experimental_capabilities` parameter when you create quantum tasks. Set this parameter to `"ALL"` to enable all available experimental features for that task. The following example shows how to enable experimental capabilities when you run a circuit on a device:

```
from braket.aws import AwsDevice

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

task = device.run(
   circuit,
   shots=1000,
   experimental_capabilities="ALL"
)
```

**Note**  
These features are experimental and may change without notice. Device performance may differ from published specifications, and results may vary from standard operations. You must explicitly enable experimental capabilities for each task. Tasks without this parameter will use only standard device capabilities.

**Topics**
+ [Access to local detuning on QuEra Aquila](#braket-access-local-detuning)
+ [Access to tall geometries on QuEra Aquila](#braket-access-tall-geometries)
+ [Access to tight geometries on QuEra Aquila](#braket-access-tight-geometries)
+ [Dynamic circuits on IQM devices](#braket-access-dynamic-circuits)

## Access to local detuning on QuEra Aquila
<a name="braket-access-local-detuning"></a>

Local detuning (LD) is a new, time-dependent control field with a customizable spatial pattern. The LD field affects qubits according to a customizable spatial pattern, realizing different Hamiltonians for different qubits beyond what the uniform driving field and the Rydberg-Rydberg interaction can create.

**Constraints:**

The spatial pattern of the local detuning field is customizable for each AHS program, but it is constant over the course of a program. The time series of the local detuning field must start and end at zero with all values being less than or equal to zero. Additionally, the parameters of the local detuning field are limited by numerical constraints, which can be viewed through the Braket SDK in the specific device properties section - `aquila_device.properties.paradigm.rydberg.rydbergLocal`.

**Limitations:**

When running quantum programs that use the local detuning field (even if its magnitude is set to constant zero in the Hamiltonian), the device experiences faster decoherence than the T2 time listed in the performance section of Aquila's properties. When unnecessary, it is best practice to omit the local detuning field from the Hamiltonian of the AHS program.

![\[Analog hamiltonian simulation in spin terminology, where there are qubits, a time dependent global driving field, and time dependent local detuning.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/AHS_spin_terminology.png)


**Examples:**

1. **Simulating the effect of non-uniform longitudinal magnetic field in spin systems**

   While the amplitude and phase of the driving field have the same effect on the qubits as the transverse magnetic field on spins, the sum of the driving field's detuning and the local detuning produces the same effect on the qubits as the longitudinal field on spins. With the spatial control over the local detuning field, more complex spin systems can be simulated.

1. **Preparing non-equilibrium initial states**

   The example notebook [Simulating lattice gauge theory with Rydberg atoms](https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/analog_hamiltonian_simulation/07_Simulating_Lattice_Gauge_Theory_with_Rydberg_Atoms.ipynb) shows how to suppress the central atom of a 9-atom linear arrangement from being excited when annealing the system towards the Z2 ordered phase. After the preparation step, the local detuning field is ramped down, and the AHS program continues to simulate the time evolution of the system starting from this particular non-equilibrium state.

1. **Solving weighted optimization problems**

   The example notebook [Maximum weight independent set](https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/analog_hamiltonian_simulation/08_Maximum_Weight_Independent_Set.ipynb) (MWIS) shows how to solve a MWIS problem on Aquila. The local detuning field is used to define the weights on the nodes of the unit disk graph, whose edges are realized by the Rybderg-blockage effect. Starting from the uniform ground state, and gradually ramping up the local detuning field makes the system transition into the ground state of the MWIS Hamiltonian to find solutions to the problem.

## Access to tall geometries on QuEra Aquila
<a name="braket-access-tall-geometries"></a>

The tall geometries feature allows you to specify geometries with increased height. With this capability, the atom arrangements of your AHS programs can span an additional length in the y direction beyond Aquila's regular capabilities.

**Constraints:**

The max height for tall geometries is 0.000128 m (128 um).

**Limitations:**

The capabilities shown on the device properties page and the `GetDevice` call will continue to reflect the regular, lower limit on the height. When an AHS program uses atom arrangements that go beyond the regular capabilities, the filling error is expected to increase. You will find an elevated number of unexpected 0s in the `pre_sequence` part of the task result, in turn, lowering the chance to get a perfectly initialized arrangement. This effect is strongest in rows with many atoms.

![\[The three dot graphs show depictions of tall geometries in a 1d line, ladder, and multiplex forms.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/tall-geometry-graphs.PNG)


**Examples:**

1. **Bigger 1d and quasi-1d arrangements**

   Atom chains and ladder-like arrangements can be extended to higher atom numbers. By orienting the long direction parallel to y allows for programming longer instances of these models.

1. **More room for multiplexing the execution of tasks with small geometries**

   The example notebook [Parallel quantum tasks on Aquila](https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/analog_hamiltonian_simulation/03_Parallel_tasks_on_Aquila.ipynb) shows how to make the most out of the available area: by placing multiplexed copies of the geometry in question in one atom arrangement. With the more available area, more copies can be placed.

## Access to tight geometries on QuEra Aquila
<a name="braket-access-tight-geometries"></a>

The tight geometries feature allows you to specify geometries with shorter spacing between neighboring rows. In an AHS program, atoms are arranged in rows, separated by a minimal vertical spacing. The y coordinate of any two atom sites must be either zero (same row), or differ by more than the minimal row spacing (different row). With the tight geometries capability, the minimal row spacing is reduced, enabling the creation of tighter atom arrangements. While this extension does not change the minimal Euclidean distance requirement between atoms, it allows the creation of lattices where distant atoms occupy neighboring rows closer to each other, a notable example is the triangle lattice.

**Constraints:**

The minimal row spacing for tight geometries is 0.000002 m (2 um).

**Limitations:**

The capabilities shown on the device properties page and the `GetDevice` call will continue to reflect the regular, higher limit on the spacing. When an AHS program uses atom arrangements that go beyond the regular capabilities, the filling error is expected to increase. Customers will find an elevated number of unexpected 0s in the `pre_sequence` part of the task result, in turn, lowering the chance to get a perfectly initialized arrangement. This effect is strongest in rows with many atoms.

![\[The graphs shows a tight geometry of a triangle lattice of dots on the left and the right graph is a hexagonal lattice of dots.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/tight-geometry-graphs.PNG)


**Examples:**

1. **Non-rectangular lattices with small lattice constants**

   Tighter row spacing allows the creation of lattices where the closest neighbor to some atoms are in the diagonal direction. Notable examples are triangular, hexagonal, and Kagome lattices and some quasi-crystals.

1. **Tunable family of lattices**

   In AHS programs, interactions are tuned by adjusting the distance between pairs of atoms. Tighter row spacing allow tuning the interactions of different atom pairs relative to each other with more freedom, since the angles and distances that define the atom structure are less limited by the minimal row spacing constraint. A notable example is the family of Shastry-Sutherland lattices with different bond lengths.

## Dynamic circuits on IQM devices
<a name="braket-access-dynamic-circuits"></a>

Dynamic circuits on IQM devices enable mid-circuit measurements (MCM) and feed-forward operations. These features allow quantum researchers and developers to implement advanced quantum algorithms with conditional logic and qubit reuse capabilities. This experimental feature helps explore quantum algorithms with improved resource efficiency and study quantum error mitigation and error correction schemes.

**Key instructions:**
+ `measure_ff`: Implements measurement for feed-forward control, measuring a qubit and storing the result with a feedback key.
+ `cc_prx`: Implements a classically-controlled rotation that applies only when the result associated with the given feedback key measures a \$11⟩ state.

Amazon Braket supports dynamic circuits through OpenQASM, the Amazon Braket SDK, and the Amazon Braket Qiskit Provider.

**Constraints:**

1. Feedback keys in the `measure_ff` instructions must be unique.

1. A `cc_prx` must happen after `measure_ff` with the same feedback key.

1. In a single circuit, the feed-forward on a qubit can only be controlled by one qubit, either by itself or by another qubit. In different circuits, you can have different pairs of control.<a name="lower"></a>

   1. For example, if qubit 1 is controlled by qubit 2, it cannot be controlled by qubit 3 in the same circuit. There is no constraint on how many times the control is applied between qubit 1 and qubit 2. Qubit 2 can be controlled by qubit 3 (or qubit 1), unless an active reset was performed on qubit 2. 

1. Control can only be applied to qubits within the same group. The qubit groups for the IQM Garnet and Emerald devices are in the following images. 

1. Programs with these capabilities must be submitted as verbatim programs. To learn more about verbatim programs, see [Verbatim compilation with OpenQASM 3.0](https://docs.aws.amazon.com/braket/latest/developerguide/braket-openqasm-verbatim-compilation.html).

**Limitations:**

MCM can only be use for feed-forward control in a program. The MCM outcomes (0 or 1) are not returned as part of a task result.

![\[The left image is the IQM Garnet 20-qubit device with 2 qubit groups in a square lattice, and the right image is the IQM Emerald 54-qubit device with 4 qubit groups in a square lattice.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/IQM-Garnet-Emerald-qubit-grouping.png)


These images display the qubit groupings for both IQM devices. The Garnet 20-qubit device contains 2 groups of qubits, while the Emerald 54-qubit device contains 4 groups of qubits. 

**Examples:**

1. **Qubit reuse through active reset**

   MCM with conditional reset operations enable qubit reuse within a single circuit execution. This reduces circuit depth requirements and improves quantum device resource utilization.

1. **Active bit flip protection**

   Dynamic circuits detect bit flip errors and apply corrective operations based on measurement outcomes. This implementation serves as a quantum error detection experiment.

1. **Teleportation experiments**

   State teleportation transfers qubit states using local quantum operations and classical information from MCMs. Gate teleportation implements gates between qubits without direct quantum operations. These experiments demonstrate foundational subroutines in three key areas: quantum error correction, measurement-based quantum computing, and quantum communication.

1. **Open quantum systems simulation**

   Dynamic circuits model noise in quantum systems through data qubit and environment entanglement, and environmental measurements. This approach uses specific qubits to represent data and environment elements. A Noise channel can be designed by the gates and measurements applied on the environment.

For more information on using dynamic circuits, see additional examples in the [Amazon Braket notebook repository](https://github.com/amazon-braket/amazon-braket-examples/tree/main/examples/experimental_capabilities/dynamic_circuits).

# Pulse control on Amazon Braket
<a name="braket-pulse-control"></a>

Pulses are the analog signals that control the qubits in a quantum computer. With certain devices on Amazon Braket, you can access the pulse control feature to submit circuits using pulses. You can access pulse control through the Braket SDK, using OpenQASM 3.0, or directly through the Braket APIs. First, introduce some key concepts for pulse control in Braket.

**Topics**
+ [Frames](#braket-frame)
+ [Ports](#braket-port)
+ [Waveforms](#braket-waveform)
+ [Working with Hello Pulse](braket-hello-pulse.md)
+ [Accessing native gates using pulses](braket-native-gate-pulse.md)

## Frames
<a name="braket-frame"></a>

A frame is a software abstraction that acts as both a clock within the quantum program and a phase. The clock time is incremented on each usage and a stateful carrier signal that is defined by a frequency. When transmitting signals to the qubit, a frame determines the qubit's carrier frequency, phase offset, and the time at which the waveform envelope is emitted. In Braket Pulse, constructing frames depends on the device, frequency, and phase. Depending on the device, you can either choose a predefined frame or instantiate new frames by providing a port.

```
from braket.aws import AwsDevice
from braket.pulse import Frame, Port

# Predefined frame from a device
device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3")
drive_frame = device.frames["Transmon_5_charge_tx"]

# Create a custom frame
readout_frame = Frame(frame_id="r0_measure", port=Port("channel_0", dt=1e-9), frequency=5e9, phase=0)
```

## Ports
<a name="braket-port"></a>

A port is a software abstraction representing any input/output hardware component controlling qubits. It helps hardware vendors provide an interface with which users can interact to manipulate and observe qubits. Ports are characterized by a single string that represents the name of the connector. This string also exposes a minimum time increment that specifies how finely we can define the waveforms.

```
from braket.pulse import Port

Port0 = Port("channel_0", dt=1e-9)
```

## Waveforms
<a name="braket-waveform"></a>

A waveform is a time-dependent envelope that we can use to emit signals on an output port or capture signals through an input port. You can specify your waveforms directly either through a list of complex numbers or by using a waveform template to generate a list from the hardware provider.

```
from braket.pulse import ArbitraryWaveform, ConstantWaveform
import numpy as np

cst_wfm = ConstantWaveform(length=1e-7, iq=0.1)
arb_wf = ArbitraryWaveform(amplitudes=np.linspace(0, 100))
```

 Braket Pulse provides a standard library of waveforms, including a constant waveform, a Gaussian waveform, and a Derivative Removal by Adiabatic Gate (DRAG) waveform. You can retrieve the waveform data through the `sample` function to draw the shape of the waveform as shown in the following example.

```
from braket.pulse import GaussianWaveform
import numpy as np
import matplotlib.pyplot as plt

zero_at_edge1 = GaussianWaveform(1e-7, 25e-9, 0.1, True)
# or zero_at_edge1 = GaussianWaveform(1e-7, 25e-9, 0.1)
zero_at_edge2 = GaussianWaveform(1e-7, 25e-9, 0.1, False)

times_1 = np.arange(0, zero_at_edge1.length, drive_frame.port.dt)
times_2 = np.arange(0, zero_at_edge2.length, drive_frame.port.dt)

plt.plot(times_1, zero_at_edge1.sample(drive_frame.port.dt))
plt.plot(times_2, zero_at_edge2.sample(drive_frame.port.dt))
```

![\[Graph showing amplitude over time for two cases: ZaE = True (lower curve) and ZaE = False (top curve). The curves have a bell shape peaking around 0.5 seconds with an amplitude of 0.10 a. u..\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/gaussianwaveform.png)


The preceding image depicts the Gaussian waveforms created from `GaussianWaveform`. We chose a pulse length of 100 ns, a width of 25 ns, and an amplitude of 0.1 (arbitrary units). The waveforms are centered in the pulse window. `GaussianWaveform` accepts a boolean argument `zero_at_edges` (ZaE in the legend). When set to `True`, this argument offsets the Gaussian waveform such that the points at t=0 and t=`length` are at zero and rescales its amplitude such that the maximum value corresponds to the `amplitude` argument.

# Working with Hello Pulse
<a name="braket-hello-pulse"></a>

In this section, you will learn how to characterize and construct a single qubit gate directly using pulse on a Rigetti device. Applying an electromagnetic field to a qubit leads to Rabi oscillation, switching qubits between its 0 state and 1 state. With calibrated length and phase of the pulse, the Rabi oscillation can calculate a single qubit gates. Here, we will determine the optimal pulse length to measure a pi/2 pulse, an elementary block used to build more complex pulse sequences.

First, to build a pulse sequence, import the `PulseSequence` class.

```
from braket.aws import AwsDevice
from braket.circuits import FreeParameter
from braket.devices import Devices
from braket.pulse import PulseSequence, GaussianWaveform

import numpy as np
```

Next, instantiate a new Braket device using the Amazon Resource Name (ARN) of the QPU. The following code block uses Rigetti Ankaa-3.

```
device = AwsDevice(Devices.Rigetti.Ankaa3)
```

The following pulse sequence includes two components: Playing a waveform and measuring a qubit. Pulse sequence can usually be applied to frames. With some exceptions such as barrier and delay, which can be applied to qubits. Before constructing the pulse sequence you must retrieve the available frames. The drive frame is used for applying the pulse for Rabi oscillation, and the readout frame is for measuring the qubit state. This example, uses the frames of qubit 25.

```
drive_frame = device.frames["Transmon_25_charge_tx"]
readout_frame = device.frames["Transmon_25_readout_rx"]
```

Now, create the waveform that will play in the drive frame. The goal is to characterize the behavior of the qubits for different pulse lengths. You will play a waveform with different lengths each time. Instead of instantiating a new waveform each time, use the Braket-supported `FreeParameter` in pulse sequence. You are able to create the waveform and the pulse sequence once with a free parameters, and then run the same pulse sequence with different input values. 

```
waveform = GaussianWaveform(FreeParameter("length"), FreeParameter("length") * 0.25, 0.2, False)
```

Finally, put them together as a pulse sequence. In the pulse sequence, `play` plays the specified waveform on the drive frame, and the `capture_v0` measures the state from the readout frame.

```
pulse_sequence = (
    PulseSequence()
    .play(drive_frame, waveform)
    .capture_v0(readout_frame)
)
```

Scan across a range of pulse length and submit them to the QPU. Before executing the pulse sequences on a QPU, bind the value of free parameters.

```
start_length = 12e-9
end_length = 2e-7
lengths = np.arange(start_length, end_length, 12e-9)
N_shots = 100

tasks = [
    device.run(pulse_sequence(length=length), shots=N_shots)
    for length in lengths
]

probability_of_zero = [
    task.result().measurement_counts['0']/N_shots
    for task in tasks
]
```

The statistics of the qubit measurement exhibits the oscillatory dynamics of the qubit that oscillates between the 0 state and the 1 state. From the measurement data, you can extract the Rabi frequency and fine tune the length of the pulse to implement a particular 1-qubit gate. For example, from the data in figure below, the periodicity is about 154 ns. So a pi/2 rotation gate would correspond to the pulse sequence with length=38.5ns. 

![\[Line graph that shows the amount of population to the pulse duration in seconds. There are two peaks and one trough in the graph.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/Rabi-frequency.png)


## Hello Pulse using OpenPulse
<a name="braket-hello-pulse-openpulse"></a>

 [OpenPulse](https://openqasm.com/language/openpulse.html) is a language for specifying pulse-level control of a general quantum device and is part of the OpenQASM 3.0 specification. Amazon Braket supports OpenPulse for directly programming pulses using the OpenQASM 3.0 representation.

 Braket uses OpenPulse as the underlying intermediate representation for expressing pulses in native instructions. OpenPulse supports the addition of instruction calibrations in the form of `defcal` (short for “define calibration”) declarations. With these declarations, you can specify an implementation of a gate instruction within a lower-level control grammar.

You can view the OpenPulse program of a Braket `PulseSequence` using the following command.

```
print(pulse_sequence.to_ir())
```

You can also construct an OpenPulse program directly.

```
from braket.ir.openqasm import Program
 
openpulse_script = """
OPENQASM 3.0;
cal {
    bit[1] psb;
    waveform my_waveform = gaussian(12.0ns, 3.0ns, 0.2, false);
    play(Transmon_25_charge_tx, my_waveform);
    psb[0] = capture_v0(Transmon_25_readout_rx);
}
"""
```

Create a `Program` object with your script. Then, submit the program to a QPU.

```
from braket.aws import AwsDevice
from braket.devices import Devices
from braket.ir.openqasm import Program

program = Program(source=openpulse_script)

device = AwsDevice(Devices.Rigetti.Ankaa3)
task = device.run(program, shots=100)
```

# Accessing native gates using pulses
<a name="braket-native-gate-pulse"></a>

Researchers often need to know exactly how the *native* gates supported by a particular QPU are implemented as pulses. Pulse sequences are carefully calibrated by hardware providers, but accessing them provides researchers the opportunity to design better gates or explore protocols for error mitigation such as zero noise extrapolation by stretching the pulses of specific gates.

Amazon Braket supports programmatic access to native gates from Rigetti.

```
import math
from braket.aws import AwsDevice
from braket.circuits import Circuit, GateCalibrations, QubitSet
from braket.circuits.gates import Rx

device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3")

calibrations = device.gate_calibrations
print(f"Downloaded {len(calibrations)} calibrations.")
```

**Note**  
Hardware providers periodically calibrate the QPU, often more than once a day. The Braket SDK enables you to obtain the latest gate calibrations.

```
device.refresh_gate_calibrations()
```

To retrieve a given native gate, such as the RX or XY gate, you need to pass the `Gate` object and the qubits of interest. For example, you can inspect the pulse implementation of the RX(π/2) applied on qubit 0.

```
rx_pi_2_q0 = (Rx(math.pi/2), QubitSet(0))

pulse_sequence_rx_pi_2_q0 = calibrations.pulse_sequences[rx_pi_2_q0]
```

You can create a filtered set of calibrations using the `filter` function. You pass a list of gates or a list of `QubitSet`. The following code creates two sets that contain all of the calibrations for RX(π/2) and for qubit 0.

```
rx_calibrations = calibrations.filter(gates=[Rx(math.pi/2)])
q0_calibrations = calibrations.filter(qubits=QubitSet([0]))
```

Now you can provide or modify the action of native gates by attaching a custom calibration set. For example, consider the following circuit.

```
bell_circuit = (
    Circuit()
    .rx(0, math.pi/2)
    .rx(1, math.pi/2)
    .iswap(0, 1)
    .rx(1, -math.pi/2)
)
```

You can run it with a custom gate calibration for the `rx` gate on `qubit 0` by passing a dictionary of `PulseSequence` objects to the `gate_definitions` keyword argument. You can construct a dictionary from the attribute `pulse_sequences` of the `GateCalibrations` object. All gates not specified are replaced with the quantum hardware provider's pulse calibration.

```
nb_shots = 50
custom_calibration = GateCalibrations({rx_pi_2_q0: pulse_sequence_rx_pi_2_q0})
task = device.run(bell_circuit, gate_definitions=custom_calibration.pulse_sequences, shots=nb_shots)
```

# Analog Hamiltonian Simulation
<a name="braket-analog-hamiltonian-simulation"></a>

[Analog Hamiltonian Simulation](https://en.wikipedia.org/wiki/Hamiltonian_simulation) (AHS) is an emerging paradigm in quantum computing that differs significantly from the traditional quantum circuit model. Instead of a sequence of gates, where each circuit acts only on a couple of qubits at a time. An AHS program is defined by the time-dependent and space-dependent parameters of the Hamiltonian in question. The [Hamiltonian of a system](https://en.wikipedia.org/wiki/Hamiltonian_(quantum_mechanics)) encodes its energy levels and the effects of external forces, which together govern the time evolution of its states. For an N-qubit systems, the Hamiltonian can be represented by a 2NX2N square matrix of complex numbers.

Quantum devices capable of performing AHS are designed to closely approximate the time evolution of a quantum system under a custom Hamiltonian by carefully tuning their internal control parameters. Such as, adjusting the amplitude and detuning parameters of a coherent driving field. The AHS paradigm is well-suited for simulating the static and dynamic properties of quantum systems with many interacting particles, such as in condensed matter physics or quantum chemistry. Purpose-built quantum processing units (QPUs), like the [Aquila device](https://aws.amazon.com/braket/quantum-computers/quera/) from QuEra, have been developed to use the power of AHS and tackle problems beyond the reach of conventional digital quantum computing approaches in innovative ways.

**Topics**
+ [Hello AHS: Run your first Analog Hamiltonian Simulation](braket-get-started-hello-ahs.md)
+ [Submit an analog program using QuEra Aquila](braket-quera-submitting-analog-program-aquila.md)

# Hello AHS: Run your first Analog Hamiltonian Simulation
<a name="braket-get-started-hello-ahs"></a>

This section provides information on running your first Analog Hamiltonian Simulation.

**Topics**
+ [Interacting spin chain](#braket-get-started-interacting-spin-chain)
+ [Arrangement](#braket-get-started-arrangement)
+ [Interaction](#braket-get-started-interaction)
+ [Driving field](#braket-get-started-driving-field)
+ [AHS program](#braket-get-started-ahs-program)
+ [Running on local simulator](#braket-get-started-running-local-simulator)
+ [Analyzing simulator results](#braket-get-started-analyzing-simulator-results)
+ [Running on QuEra's Aquila QPU](#braket-get-started-running-aquila-qpu)
+ [Analyzing QPU results](#braket-get-started-analyzing-qpu-results)
+ [Next steps](#braket-get-started-ahs-next)

## Interacting spin chain
<a name="braket-get-started-interacting-spin-chain"></a>

For a canonical example of a system of many interacting particles, let us consider a ring of eight spins (each of which can be in “up” ∣↑⟩ and “down” ∣↓⟩ states). Albeit small, this model system already exhibits a handful of interesting phenomena of naturally occurring magnetic materials. In this example, we will show how to prepare a so-called anti-ferromagnetic order, where consecutive spins point in opposite directions.

![\[Diagram connecting 8 circle nodes that contain inversing up and down arrows.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/AntiFerromagnetic.png)


## Arrangement
<a name="braket-get-started-arrangement"></a>

We will use one neutral atom to stand for each spin, and the “up” and “down” spin states will be encoded in excited Rydberg state and ground state of the atoms, respectively. First, we create the 2-d arrangement. We can program the above ring of spins with the following code.

 **Prerequisites**: You need to pip install the [Braket SDK](https://github.com/aws/amazon-braket-sdk-python#installing-the-amazon-braket-python-sdk). (If you are using a Braket hosted notebook instance, this SDK comes pre-installed with the notebooks.) To reproduce the plots, you also need to separately install matplotlib with the shell command `pip install matplotlib`.

```
from braket.ahs.atom_arrangement import AtomArrangement
import numpy as np
import matplotlib.pyplot as plt  # Required for plotting

a = 5.7e-6  # Nearest-neighbor separation (in meters)

register = AtomArrangement()
register.add(np.array([0.5, 0.5 + 1/np.sqrt(2)]) * a)
register.add(np.array([0.5 + 1/np.sqrt(2), 0.5]) * a)
register.add(np.array([0.5 + 1/np.sqrt(2), - 0.5]) * a)
register.add(np.array([0.5, - 0.5 - 1/np.sqrt(2)]) * a)
register.add(np.array([-0.5, - 0.5 - 1/np.sqrt(2)]) * a)
register.add(np.array([-0.5 - 1/np.sqrt(2), - 0.5]) * a)
register.add(np.array([-0.5 - 1/np.sqrt(2), 0.5]) * a)
register.add(np.array([-0.5, 0.5 + 1/np.sqrt(2)]) * a)
```

which we can also plot with

```
fig, ax = plt.subplots(1, 1, figsize=(7, 7))
xs, ys = [register.coordinate_list(dim) for dim in (0, 1)]
ax.plot(xs, ys, 'r.', ms=15)

for idx, (x, y) in enumerate(zip(xs, ys)):
    ax.text(x, y, f" {idx}", fontsize=12)

plt.show()  # This will show the plot below in an ipython or jupyter session
```

![\[Scatter plot showing points distributed across positive and negative values on both axes.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/PlotNeutralAtoms.png)


## Interaction
<a name="braket-get-started-interaction"></a>

To prepare the anti-ferromagnetic phase, we need to induce interactions between neighboring spins. We use the [van der Waals interaction](https://en.wikipedia.org/wiki/Van_der_Waals_force) for this, which is natively implemented by neutral atom devices (such as the Aquila device from QuEra). Using the spin-representation, the Hamiltonian term for this interaction can be expressed as a sum over all spin pairs (j,k).

![\[Hamiltonian interaction equation showing this this interaction as expressed as a sum over all spin pairs (j,k).\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/HInteraction.png)


Here, nj​=∣↑j​⟩⟨↑j​∣ is an operator that takes the value of 1 only if spin j is in the “up” state, and 0 otherwise. The strength is Vj,k​=C6​/(dj,k​)6, where C6​ is the fixed coefficient, and dj,k​ is the Euclidean distance between spins j and k. The immediate effect of this interaction term is that any state where both spin j and spin k are “up” have elevated energy (by the amount Vj,k​). By carefully designing the rest of the AHS program, this interaction will prevent neighboring spins from both being in the “up” state, an effect commonly known as "Rydberg blockade."

## Driving field
<a name="braket-get-started-driving-field"></a>

At the beginning of the AHS program, all spins (by default) start in their “down” state, they are in a so-called ferromagnetic phase. Keeping an eye on our goal to prepare the anti-ferromagnetic phase, we specify a time-dependent coherent driving field that smoothly transitions the spins from this state to a many-body state where the “up” states are preferred. The corresponding Hamiltonian can be written as

![\[Mathematical equation depicting the calculation of a Hamiltonian drive function.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/HDrive.png)


where Ω(t),ϕ(t),Δ(t) are the time-dependent, global amplitude (aka [Rabi frequency](https://en.wikipedia.org/wiki/Rabi_frequency)), phase, and detuning of the driving field affecting all spins uniformly. Here S−,k​=∣↓k​⟩⟨↑k​∣and S\$1,k​​=(S−,k​)†=∣↑k​⟩⟨↓k​∣ are the lowering and raising operators of spin k, respectively, and nk​=∣↑k​⟩⟨↑k​∣ is the same operator as before. The Ω part of the driving field coherently couples the “down” and the “up” states of all spins simultaneously, while the Δ part controls the energy reward for “up” states.

To program a smooth transition from the ferromagnetic phase to the anti-ferromagnetic phase, we specify the driving field with the following code.

```
from braket.timings.time_series import TimeSeries
from braket.ahs.driving_field import DrivingField

# Smooth transition from "down" to "up" state
time_max = 4e-6  # seconds
time_ramp = 1e-7  # seconds
omega_max = 6300000.0  # rad / sec
delta_start = -5 * omega_max
delta_end = 5 * omega_max

omega = TimeSeries()
omega.put(0.0, 0.0)
omega.put(time_ramp, omega_max)
omega.put(time_max - time_ramp, omega_max)
omega.put(time_max, 0.0)

delta = TimeSeries()
delta.put(0.0, delta_start)
delta.put(time_ramp, delta_start)
delta.put(time_max - time_ramp, delta_end)
delta.put(time_max, delta_end)

phi = TimeSeries().put(0.0, 0.0).put(time_max, 0.0)

drive = DrivingField(
   amplitude=omega,
   phase=phi,
   detuning=delta
)
```

We can visualize the time series of the driving field with the following script.

```
fig, axes = plt.subplots(3, 1, figsize=(12, 7), sharex=True)

ax = axes[0]
time_series = drive.amplitude.time_series
ax.plot(time_series.times(), time_series.values(), '.-')
ax.grid()
ax.set_ylabel('Omega [rad/s]')

ax = axes[1]
time_series = drive.detuning.time_series
ax.plot(time_series.times(), time_series.values(), '.-')
ax.grid()
ax.set_ylabel('Delta [rad/s]')

ax = axes[2]
time_series = drive.phase.time_series
# Note: time series of phase is understood as a piecewise constant function
ax.step(time_series.times(), time_series.values(), '.-', where='post')
ax.set_ylabel('phi [rad]')
ax.grid()
ax.set_xlabel('time [s]')

plt.show()  # This will show the plot below in an ipython or jupyter session
```

![\[Three graphs showing phi, delta, and omega over time. The top subplot shows the growth to just above 6 rads/s where it stays for 4 seconds until it drops back to 0. The middle subplot depicts the associated linear growth of the derivative, and the bottom subplot illustrates a flat line near zero.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/DrivingTimeSeries.png)


## AHS program
<a name="braket-get-started-ahs-program"></a>

The register, the driving field, (and the implicit van der Waals interactions) make up the Analog Hamiltonian Simulation program `ahs_program`.

```
from braket.ahs.analog_hamiltonian_simulation import AnalogHamiltonianSimulation

ahs_program = AnalogHamiltonianSimulation(
   register=register,
   hamiltonian=drive
)
```

## Running on local simulator
<a name="braket-get-started-running-local-simulator"></a>

Since this example is small (less than 15 spins), before running it on an AHS-compatible QPU, we can run it on the local AHS simulator which comes with the Braket SDK. Since the local simulator is available for free with the Braket SDK, this is best practice to ensure that our code can correctly execute.

Here, we can set the number of shots to a high value (say, 1 million) because the local simulator tracks the time evolution of the quantum state and draws samples from the final state; hence, increasing the number of shots, while increasing the total runtime only marginally.

```
from braket.devices import LocalSimulator

device = LocalSimulator("braket_ahs")

result_simulator = device.run(
   ahs_program,
   shots=1_000_000
).result()  # Takes about 5 seconds
```

## Analyzing simulator results
<a name="braket-get-started-analyzing-simulator-results"></a>

We can aggregate the shot results with the following function that infers the state of each spin (which may be “d” for “down”, “u” for “up”, or “e” for empty site), and counts how many times each configuration occurred across the shots.

```
from collections import Counter


def get_counts(result):
    """Aggregate state counts from AHS shot results

    A count of strings (of length = # of spins) are returned, where
    each character denotes the state of a spin (site):
      e: empty site
      u: up state spin
      d: down state spin

    Args:
      result (braket.tasks.analog_hamiltonian_simulation_quantum_task_result.AnalogHamiltonianSimulationQuantumTaskResult)

    Returns
       dict: number of times each state configuration is measured

    """
    state_counts = Counter()
    states = ['e', 'u', 'd']
    for shot in result.measurements:
        pre = shot.pre_sequence
        post = shot.post_sequence
        state_idx = np.array(pre) * (1 + np.array(post))
        state = "".join(map(lambda s_idx: states[s_idx], state_idx))
        state_counts.update((state,))
    return dict(state_counts)


counts_simulator = get_counts(result_simulator)  # Takes about 5 seconds
print(counts_simulator)
```

```
*[Output]*
{'dddddddd': 5, 'dddddddu': 12, 'ddddddud': 15, ...}
```

Here `counts` is a dictionary that counts the number of times each state configuration is observed across the shots. We can also visualize them with the following code.

```
from collections import Counter


def has_neighboring_up_states(state):
    if 'uu' in state:
        return True
    if state[0] == 'u' and state[-1] == 'u':
        return True
    return False


def number_of_up_states(state):
    return Counter(state)['u']


def plot_counts(counts):
    non_blockaded = []
    blockaded = []
    for state, count in counts.items():
        if not has_neighboring_up_states(state):
            collection = non_blockaded
        else:
            collection = blockaded
        collection.append((state, count, number_of_up_states(state)))

    blockaded.sort(key=lambda _: _[1], reverse=True)
    non_blockaded.sort(key=lambda _: _[1], reverse=True)

    for configurations, name in zip((non_blockaded,
                                     blockaded),
                                    ('no neighboring "up" states',
                                     'some neighboring "up" states')):
        plt.figure(figsize=(14, 3))
        plt.bar(range(len(configurations)), [item[1] for item in configurations])
        plt.xticks(range(len(configurations)))
        plt.gca().set_xticklabels([item[0] for item in configurations], rotation=90)
        plt.ylabel('shots')
        plt.grid(axis='y')
        plt.title(f'{name} configurations')
        plt.show()


plot_counts(counts_simulator)
```

![\[Bar chart showing a large number of shots with no neighboring "up" states configurations.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/AHSCounts1.png)


![\[Bar chart showing shots of some neighboring "up" states configurations, with 4 states at 1.0 shots.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/AHSCounts2.png)


From the plots, we can read the following observations the verify that we successfully prepared the anti-ferromagnetic phase.

1. Generally, non-blockaded states (where no two neighboring spins are in the “up” state) are more common than states where at least one pair of neighboring spins are both in “up” states.

1. Generally, states with more "up" excitations are favored, unless the configuration is blockaded.

1. The most common states are indeed the perfect anti-ferromagnetic states `"dudududu"` and `"udududud"`.

1. The second most common states are the ones where there is only 3 “up” excitations with consecutive separations of 1, 2, 2. This shows that the van der Waals interaction has an affect (albeit much smaller) on next-nearest neighbors too.

## Running on QuEra's Aquila QPU
<a name="braket-get-started-running-aquila-qpu"></a>

 **Prerequisites**: Apart from pip installing the Braket [SDK](https://github.com/aws/amazon-braket-sdk-python#installing-the-amazon-braket-python-sdk), if you are new to Amazon Braket, make sure that you have completed the necessary [Get Started steps](https://docs.aws.amazon.com/braket/latest/developerguide/braket-get-started.html).

**Note**  
If you are using a Braket hosted notebook instance, the Braket SDK comes pre-installed with the instance.

With all dependencies installed, we can connect to the Aquila QPU.

```
from braket.aws import AwsDevice

aquila_qpu = AwsDevice("arn:aws:braket:us-east-1::device/qpu/quera/Aquila")
```

To make our AHS program suitable for the QuEra machine, we need to round all values to comply with the levels of precision allowed by the Aquila QPU. (These requirements are governed by the device parameters with “Resolution” in their name. We can see them by executing `aquila_qpu.properties.dict()` in a notebook. For more details of capabilities and requirements of Aquila, see the [Introduction to Aquila](https://github.com/aws/amazon-braket-examples/blob/main/examples/analog_hamiltonian_simulation/01_Introduction_to_Aquila.ipynb) notebook.) We can do this by calling the `discretize` method.

```
discretized_ahs_program = ahs_program.discretize(aquila_qpu)
```

Now we can run the program (running only 100 shots for now) on the Aquila QPU.

**Note**  
Running this program on the Aquila processor will incur a cost. The Amazon Braket SDK includes a [Cost Tracker](https://aws.amazon.com/blogs/quantum-computing/managing-the-cost-of-your-experiments-in-amazon-braket/) that enables customers to set cost limits as well as track their costs in near real-time.

```
task = aquila_qpu.run(discretized_ahs_program, shots=100)

metadata = task.metadata()
task_arn = metadata['quantumTaskArn']
task_status = metadata['status']

print(f"ARN: {task_arn}")
print(f"status: {task_status}")
```

```
*[Output]*
ARN: arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef
status: CREATED
```

Due to the large variance of how long a quantum task may take to run (depending on availability windows and QPU utilization), it is a good idea to note down the quantum task ARN, so we can check its status at a later time with the following code snippet.

```
# Optionally, in a new python session
from braket.aws import AwsQuantumTask

SAVED_TASK_ARN = "arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef"

task = AwsQuantumTask(arn=SAVED_TASK_ARN)
metadata = task.metadata()
task_arn = metadata['quantumTaskArn']
task_status = metadata['status']

print(f"ARN: {task_arn}")
print(f"status: {task_status}")
```

```
*[Output]*
ARN: arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef
status: COMPLETED
```

Once the status is COMPLETED (which can also be checked from the quantum tasks page of the Amazon Braket [console](https://us-east-1.console.aws.amazon.com/braket/home?region=us-east-1#/tasks)), we can query the results with:

```
result_aquila = task.result()
```

## Analyzing QPU results
<a name="braket-get-started-analyzing-qpu-results"></a>

Using the same `get_counts` functions as before, we can compute the counts:

```
counts_aquila = get_counts(result_aquila)
   print(counts_aquila)
```

```
*[Output]*
{'dddududd': 2, 'dudududu': 18, 'ddududud': 4, ...}
```

and plot them with `plot_counts`:

```
plot_counts(counts_aquila)
```

![\[Bar chart showing a large number of shots with no neighboring "up" states configurations.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/QPUPlotCounts1.png)


![\[Bar chart showing shots of some neighboring "up" states configurations, with 4 states at 1.0 shots.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/QPUPlotCounts2.png)


Note that a small fraction of shots have empty sites (marked with “e”). This is due to a 1—2% per atom preparation imperfections of the Aquila QPU. Apart from this, the results match with the simulation within the expected statistical fluctuation due to small number of shots.

## Next steps
<a name="braket-get-started-ahs-next"></a>

Congratulations, you have now run your first AHS workload on Amazon Braket using the local AHS simulator and the Aquila QPU.

To learn more about Rydberg physics, Analog Hamiltonian Simulation and the Aquila device, refer to our [example notebooks](https://github.com/aws/amazon-braket-examples/tree/main/examples/analog_hamiltonian_simulation).

# Submit an analog program using QuEra Aquila
<a name="braket-quera-submitting-analog-program-aquila"></a>

This page provides a comprehensive documentation about the capabilities of the Aquila machine from QuEra. Details covered here are the following: 

1. The parameterized Hamiltonian simulated by Aquila

1. AHS program parameters

1. AHS result content

1. Aquila capabilities parameter

**Topics**
+ [Hamiltonian](#braket-quera-aquila-device-hamiltonian)
+ [Braket AHS program schema](#braket-quera-ahs-program-schema)
+ [Braket AHS task result schema](#braket-quera-ahs-task-result-schema)
+ [QuEra device properties schema](#braket-quera-device-properties-schema)

## Hamiltonian
<a name="braket-quera-aquila-device-hamiltonian"></a>

The Aquila machine from QuEra simulates the following (time-dependent) Hamiltonian natively:

![\[Mathematical equation with summations representing the Hamiltonian of a system, involving drive, local detuning, and interdot coupling terms.\]](http://docs.aws.amazon.com/braket/latest/developerguide/images/TimeDependentDrivingHamiltonian.png)


**Note**  
Access to local detuning is an [Experimental capability](https://docs.aws.amazon.com/braket/latest/developerguide/braket-experimental-capabilities.html) and is available by request through Braket Direct.

where
+ Hdrive,k​(t)=( 1/2 ​Ω(t)eiϕ(t)S−,k​ \$1 1/2 ​Ω(t)e−iϕ(t) S\$1,k​) \$1 (−Δglobal​(t)nk​),
  + Ω(t) is the time-dependent, global driving amplitude (aka Rabi frequency), in units of (rad / s)
  + ϕ(t) is the time-dependent, global phase, measured in radians
  + S−,k​ and S\$1,k​ are the spin lowering and raising operators of atom k (in the basis \$1↓⟩=\$1g⟩, \$1↑⟩=\$1r⟩, they are S−​=\$1g⟩⟨r\$1, S\$1​=(S−​)†=\$1r⟩⟨g\$1)
  + Δglobal​(t) is the time-dependent, global detuning
  + nk ​is the projection operator on the Rydberg state of atom k (that is, n=\$1r⟩⟨r\$1)
+ Hlocal detuning,k(t)=-Δlocal(t)hknk
  + Δlocal(t) is the time-dependent factor of the local frequency shift, in units of (rad / s)
  + hk is the site-dependent factor, a dimensionless number between 0.0 and 1.0
+ Vvdw,k,l​=C6​/(dk,l​)6nk​nl​,
  + C6​ is the van der Waals coefficient, in units of (rad / s) \$1 (m)^6
  + dk,l ​is the Euclidean distance between atom k and l, measured in meters.

Users have control over the following parameters through the Braket AHS program schema.
+ 2-d atom arrangement (xk​ and yk​ coordinates of each atom k, in units of um), which controls the pairwise atomic distances dk,l​ with k,l=1,2,…N
+ Ω(t), the time-dependent, global Rabi frequency, in units of (rad / s)
+ ϕ(t), the time-dependent, global phase, in units of (rad)
+ Δglobal​(t), the time-dependent, global detuning, in units of (rad / s)
+ Δlocal(t), the time-dependent (global) factor of the magnitude of local detuning, in units of (rad / s)
+ hk, the (static) site-dependent factor of the magnitude of local detuning, a dimensionless number between 0.0 and 1.0

**Note**  
The user cannot control which levels are involved (that is, S−​,S\$1​, n operators are fixed) nor the strength of the Rydberg-Rydberg interaction coefficient (C6​).

## Braket AHS program schema
<a name="braket-quera-ahs-program-schema"></a>

 **braket.ir.ahs.program\$1v1.Program object** (example)

**Note**  
If the [local detuning](https://docs.aws.amazon.com/braket/latest/developerguide/braket-experimental-capabilities.html#braket-access-local-detuning) feature is not enabled for your account, use `localDetuning=[]` in the following example.

```
Program(
    braketSchemaHeader=BraketSchemaHeader(
        name='braket.ir.ahs.program',
        version='1'
    ),
    setup=Setup(
        ahs_register=AtomArrangement(
            sites=[
                [Decimal('0'), Decimal('0')],
                [Decimal('0'), Decimal('4e-6')],
                [Decimal('4e-6'), Decimal('0')]
            ],
            filling=[1, 1, 1]
        )
    ),
    hamiltonian=Hamiltonian(
        drivingFields=[
            DrivingField(
                amplitude=PhysicalField(
                    time_series=TimeSeries(
                        values=[Decimal('0'), Decimal('15700000.0'), Decimal('15700000.0'), Decimal('0')],
                        times=[Decimal('0'), Decimal('0.000001'), Decimal('0.000002'), Decimal('0.000003')]
                    ),
                    pattern='uniform'
                ),
                phase=PhysicalField(
                    time_series=TimeSeries(
                        values=[Decimal('0'), Decimal('0')],
                        times=[Decimal('0'), Decimal('0.000003')]
                    ),
                    pattern='uniform'
                ),
                detuning=PhysicalField(
                    time_series=TimeSeries(
                        values=[Decimal('-54000000.0'), Decimal('54000000.0')],
                        times=[Decimal('0'), Decimal('0.000003')]
                    ),
                    pattern='uniform'
                )
            )
        ],
        localDetuning=[
            LocalDetuning(
                magnitude=PhysicalField(
                    times_series=TimeSeries(
                        values=[Decimal('0'), Decimal('25000000.0'), Decimal('25000000.0'), Decimal('0')],
                        times=[Decimal('0'), Decimal('0.000001'), Decimal('0.000002'), Decimal('0.000003')]
                    ),
                    pattern=Pattern([Decimal('0.8'), Decimal('1.0'), Decimal('0.9')])
                )
            )
        ]
    )
)
```

**JSON** (example)

**Note**  
If the [local detuning](https://docs.aws.amazon.com/braket/latest/developerguide/braket-experimental-capabilities.html#braket-access-local-detuning) feature is not enabled for your account, use `"localDetuning": []` in the following example.

```
{
    "braketSchemaHeader": {
        "name": "braket.ir.ahs.program",
        "version": "1"
    },
    "setup": {
        "ahs_register": {
            "sites": [
                [0E-7, 0E-7], 
                [0E-7, 4E-6],
                [4E-6, 0E-7]
            ],
            "filling": [1, 1, 1]
        }
    },
    "hamiltonian": {
        "drivingFields": [
            {
                "amplitude": {
                    "time_series": {
                        "values": [0.0, 15700000.0, 15700000.0, 0.0],
                        "times": [0E-9, 0.000001000, 0.000002000, 0.000003000]
                    },
                    "pattern": "uniform"
                },
                "phase": {
                    "time_series": {
                        "values": [0E-7, 0E-7],
                        "times": [0E-9, 0.000003000]
                    },
                    "pattern": "uniform"
                },
                "detuning": {
                    "time_series": {
                        "values": [-54000000.0, 54000000.0],
                        "times": [0E-9, 0.000003000]
                    },
                    "pattern": "uniform"
                }
            }
        ],
        "localDetuning": [
            {
                "magnitude": {
                    "time_series": {
                        "values": [0.0, 25000000.0, 25000000.0, 0.0],
                        "times": [0E-9, 0.000001000, 0.000002000, 0.000003000]
                    },
                    "pattern": [0.8, 1.0, 0.9]
                }
            }
        ]
    }
}
```


**Main fields**  

| Program field | type | description | 
| --- | --- | --- | 
|  setup.ahs\$1register.sites  |  List[List[Decimal]]  |  List of 2-d coordinates where the tweezers trap atoms  | 
|  setup.ahs\$1register.filling  |  List[int]  |  Marks atoms that occupy the trap sites with 1, and empty sites with 0  | 
|  hamiltonian.drivingFields[].amplitude.time\$1series.times  |  List[Decimal]  |  time points of driving amplitude, Omega(t)  | 
|  hamiltonian.drivingFields[].amplitude.time\$1series.values  |  List[Decimal]  |  values of driving amplitude, Omega(t)  | 
|  hamiltonian.drivingFields[].amplitude.pattern  |  str  |  spatial pattern of driving amplitude, Omega(t); must be 'uniform'  | 
|  hamiltonian.drivingFields[].phase.time\$1series.times  |  List[Decimal]  |  time points of driving phase, phi(t)  | 
|  hamiltonian.drivingFields[].phase.time\$1series.values  |  List[Decimal]  |  values of driving phase, phi(t)  | 
|  hamiltonian.drivingFields[].phase.pattern  |  str  |  spatial pattern of driving phase, phi(t); must be 'uniform'  | 
|  hamiltonian.drivingFields[].detuning.time\$1series.times  |  List[Decimal]  |  time points of driving detuning, Delta\$1global(t)  | 
|  hamiltonian.drivingFields[].detuning.time\$1series.values  |  List[Decimal]  |  values of driving detuning, Delta\$1global(t)  | 
|  hamiltonian.drivingFields[].detuning.pattern  |  str  |  spatial pattern of driving detuning, Delta\$1global(t); must be 'uniform'  | 
|  hamiltonian.localDetuning[].magnitude.time\$1series.times  |  List[Decimal]  |  time points of the time-dependent factor of the local detuning magnitude, Delta\$1local(t)  | 
|  hamiltonian.localDetuning[].magnitude.time\$1series.values  |  List[Decimal]  |  values of the time-dependent factor of the local detuning magnitude, Delta\$1local(t)  | 
|  hamiltonian.localDetuning[].magnitude.pattern  |  List[Decimal]  |  site-dependent factor of the local detuning magnitude, h\$1k (values corresponds to sites in setup.ahs\$1register.sites)  | 


**Metadata fields**  

| Program field | type | description | 
| --- | --- | --- | 
|  braketSchemaHeader.name  |  str  |  name of the schema; must be 'braket.ir.ahs.program'  | 
|  braketSchemaHeader.version  |  str  |  version of the schema  | 

## Braket AHS task result schema
<a name="braket-quera-ahs-task-result-schema"></a>

 **braket.tasks.analog\$1hamiltonian\$1simulation\$1quantum\$1task\$1result.AnalogHamiltonianSimulationQuantumTaskResult** (example)

```
AnalogHamiltonianSimulationQuantumTaskResult(
    task_metadata=TaskMetadata(
        braketSchemaHeader=BraketSchemaHeader(
            name='braket.task_result.task_metadata',
            version='1'
        ),
        id='arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef',
        shots=2,
        deviceId='arn:aws:braket:us-east-1::device/qpu/quera/Aquila',
        deviceParameters=None,
        createdAt='2022-10-25T20:59:10.788Z',
        endedAt='2022-10-25T21:00:58.218Z',
        status='COMPLETED',
        failureReason=None
    ),
    measurements=[
        ShotResult(
            status=<AnalogHamiltonianSimulationShotStatus.SUCCESS: 'Success'>,

            pre_sequence=array([1, 1, 1, 1]),
            post_sequence=array([0, 1, 1, 1])
        ),

        ShotResult(
            status=<AnalogHamiltonianSimulationShotStatus.SUCCESS: 'Success'>,

            pre_sequence=array([1, 1, 0, 1]),
            post_sequence=array([1, 0, 0, 0])
        )
    ]
)
```

 **JSON** (example)

```
{
    "braketSchemaHeader": {
        "name": "braket.task_result.analog_hamiltonian_simulation_task_result",
        "version": "1"
    },
    "taskMetadata": {
        "braketSchemaHeader": {
            "name": "braket.task_result.task_metadata",
            "version": "1"
        },
        "id": "arn:aws:braket:us-east-1:123456789012:quantum-task/12345678-90ab-cdef-1234-567890abcdef",
        "shots": 2,
        "deviceId": "arn:aws:braket:us-east-1::device/qpu/quera/Aquila",

        "createdAt": "2022-10-25T20:59:10.788Z",
        "endedAt": "2022-10-25T21:00:58.218Z",
        "status": "COMPLETED"

    },
    "measurements": [
        {
            "shotMetadata": {"shotStatus": "Success"},
            "shotResult": {
                "preSequence": [1, 1, 1, 1],
                "postSequence": [0, 1, 1, 1]
            }
        },
        {
            "shotMetadata": {"shotStatus": "Success"},
            "shotResult": {
                "preSequence": [1, 1, 0, 1],
                "postSequence": [1, 0, 0, 0]
            }
        }
    ],
    "additionalMetadata": {
        "action": {...}
        "queraMetadata": {
            "braketSchemaHeader": {
                "name": "braket.task_result.quera_metadata",
                "version": "1"
            },
            "numSuccessfulShots": 100
        }
    }
}
```


**Main fields**  

| Task result field | type | description | 
| --- | --- | --- | 
|  measurements[].shotResult.preSequence  |  List[int]  |  Pre-sequence measurement bits (one for each atomic site) for each shot: 0 if site is empty, 1 if site is filled, measured before the sequences of pulses that run the quantum evolution  | 
|  measurements[].shotResult.postSequence  |  List[int]  |  Post-sequence measurement bits for each shot: 0 if atom is in Rydberg state or site is empty, 1 if atom is in ground state, measured at the end of the sequences of pulses that run the quantum evolution  | 


**Metadata fields**  

| Task result field | type | description | 
| --- | --- | --- | 
|  braketSchemaHeader.name  |  str  |  name of the schema; must be 'braket.task\$1result.analog\$1hamiltonian\$1simulation\$1task\$1result'  | 
|  braketSchemaHeader.version  |  str  |  version of the schema  | 
|  taskMetadata.braketSchemaHeader.name  |  str  |  name of the schema; must be ‘braket.task\$1result.task\$1metadata'  | 
|  taskMetadata.braketSchemaHeader.version  |  str  |  version of the schema  | 
|  taskMetadata.id  |  str  |  The ID of the quantum task. For AWS quantum tasks, this is the quantum task ARN.  | 
|  taskMetadata.shots  |  int  |  The number of shots for the quantum task  | 
|  taskMetadata.shots.deviceId  |  str  |  The ID of the device on which the quantum task ran. For AWS devices, this is the device ARN.  | 
|  taskMetadata.shots.createdAt  |  str  |  The timestamp of creation; the format must be in ISO-8601/RFC3339 string format YYYY-MM-DDTHH:mm:ss.sssZ. Default is None.  | 
|  taskMetadata.shots.endedAt  |  str  |  The timestamp of when the quantum task ended; the format must be in ISO-8601/RFC3339 string format YYYY-MM-DDTHH:mm:ss.sssZ. Default is None.  | 
|  taskMetadata.shots.status  |  str  |  The status of the quantum task (CREATED, QUEUED, RUNNING, COMPLETED, FAILED). Default is None.  | 
|  taskMetadata.shots.failureReason  |  str  |  The failure reason of the quantum task. Default is None.  | 
|  additionalMetadata.action  |  braket.ir.ahs.program\$1v1.Program  |  (See the [Braket AHS program schema](#braket-quera-ahs-program-schema) section)  | 
|  additionalMetadata.action.braketSchemaHeader.queraMetadata.name  |  str  |  name of the schema; must be 'braket.task\$1result.quera\$1metadata'  | 
|  additionalMetadata.action.braketSchemaHeader.queraMetadata.version  |  str  |  version of the schema  | 
|  additionalMetadata.action.numSuccessfulShots  |  int  |  number of completely successful shots; must be equal to the requested number of shots  | 
|  measurements[].shotMetadata.shotStatus  |  int  |  The status of the shot, (Success, Partial success, Failure); must be "Success"  | 

## QuEra device properties schema
<a name="braket-quera-device-properties-schema"></a>

 **braket.device\$1schema.quera.quera\$1device\$1capabilities\$1v1.QueraDeviceCapabilities** (example)

```
QueraDeviceCapabilities(
    service=DeviceServiceProperties(
        braketSchemaHeader=BraketSchemaHeader(
            name='braket.device_schema.device_service_properties', 
            version='1'
            ), 
            executionWindows=[
                DeviceExecutionWindow(
                    executionDay=<ExecutionDay.MONDAY: 'Monday'>, 
                    windowStartHour=datetime.time(1, 0), 
                    windowEndHour=datetime.time(23, 59, 59)
                ), 
                DeviceExecutionWindow(
                    executionDay=<ExecutionDay.TUESDAY: 'Tuesday'>, 
                    windowStartHour=datetime.time(0, 0), 
                    windowEndHour=datetime.time(12, 0)
                ), 
                DeviceExecutionWindow(
                    executionDay=<ExecutionDay.WEDNESDAY: 'Wednesday'>, 
                    windowStartHour=datetime.time(0, 0), 
                    windowEndHour=datetime.time(12, 0)
                ), 
                DeviceExecutionWindow(
                    executionDay=<ExecutionDay.FRIDAY: 'Friday'>, 
                    windowStartHour=datetime.time(0, 0), 
                    windowEndHour=datetime.time(23, 59, 59)
                ), 
                DeviceExecutionWindow(
                    executionDay=<ExecutionDay.SATURDAY: 'Saturday'>, 
                    windowStartHour=datetime.time(0, 0), 
                    windowEndHour=datetime.time(23, 59, 59)
                ), 
                DeviceExecutionWindow(
                    executionDay=<ExecutionDay.SUNDAY: 'Sunday'>, 
                    windowStartHour=datetime.time(0, 0), 
                    windowEndHour=datetime.time(12, 0)
                )
            ], 
            shotsRange=(1, 1000), 
            deviceCost=DeviceCost(
                price=0.01, 
                unit='shot'
            ), 
            deviceDocumentation=
                DeviceDocumentation(
                    imageUrl='https://a.b.cdn.console.awsstatic.com/59534b58c709fc239521ef866db9ea3f1aba73ad3ebcf60c23914ad8c5c5c878/a6cfc6fca26cf1c2e1c6.png', 
                    summary='Analog quantum processor based on neutral atom arrays', 
                    externalDocumentationUrl='https://www.quera.com/aquila'
                ), 
                deviceLocation='Boston, USA', 
                updatedAt=datetime.datetime(2024, 1, 22, 12, 0, tzinfo=datetime.timezone.utc), 
                getTaskPollIntervalMillis=None
    ), 
    action={
        <DeviceActionType.AHS: 'braket.ir.ahs.program'>: DeviceActionProperties(
                version=['1'], 
                actionType=<DeviceActionType.AHS: 'braket.ir.ahs.program'>
            )
    }, 
    deviceParameters={}, 
    braketSchemaHeader=BraketSchemaHeader(
        name='braket.device_schema.quera.quera_device_capabilities', 
        version='1'
    ), 
    paradigm=QueraAhsParadigmProperties(
        ...
        # See https://github.com/amazon-braket/amazon-braket-schemas-python/blob/main/src/braket/device_schema/quera/quera_ahs_paradigm_properties_v1.py
        ...
    )  
)
```

 **JSON** (example)

```
{
    "service": {
        "braketSchemaHeader": {
            "name": "braket.device_schema.device_service_properties",
            "version": "1"
        },
        "executionWindows": [
            {
                "executionDay": "Monday",
                "windowStartHour": "01:00:00",
                "windowEndHour": "23:59:59"
            },
            {
                "executionDay": "Tuesday",
                "windowStartHour": "00:00:00",
                "windowEndHour": "12:00:00"
            },
            {
                "executionDay": "Wednesday",
                "windowStartHour": "00:00:00",
                "windowEndHour": "12:00:00"
            },
            {
                "executionDay": "Friday",
                "windowStartHour": "00:00:00",
                "windowEndHour": "23:59:59"
            },
            {
                "executionDay": "Saturday",
                "windowStartHour": "00:00:00",
                "windowEndHour": "23:59:59"
            },
            {
                "executionDay": "Sunday",
                "windowStartHour": "00:00:00",
                "windowEndHour": "12:00:00"
            }
        ],
        "shotsRange": [
            1,
            1000
        ],
        "deviceCost": {
            "price": 0.01,
            "unit": "shot"
        },
        "deviceDocumentation": {
            "imageUrl": "https://a.b.cdn.console.awsstatic.com/59534b58c709fc239521ef866db9ea3f1aba73ad3ebcf60c23914ad8c5c5c878/a6cfc6fca26cf1c2e1c6.png",
            "summary": "Analog quantum processor based on neutral atom arrays",
            "externalDocumentationUrl": "https://www.quera.com/aquila"
        },
        "deviceLocation": "Boston, USA",
        "updatedAt": "2024-01-22T12:00:00+00:00"
    },
    "action": {
        "braket.ir.ahs.program": {
            "version": [
                "1"
            ],
            "actionType": "braket.ir.ahs.program"
        }
    },
    "deviceParameters": {},
    "braketSchemaHeader": {
        "name": "braket.device_schema.quera.quera_device_capabilities",
        "version": "1"
    },
    "paradigm": {
        ...
        # See Aquila device page > "Calibration" tab > "JSON" page
        ...
    }
}
```


**Service properties fields**  

| Service properties field | type | description | 
| --- | --- | --- | 
|  service.executionWindows[].executionDay  |  ExecutionDay  |  Days of the execution window; must be 'Everyday', 'Weekdays', 'Weekend', 'Monday', 'Tuesday', 'Wednesday', Thursday', 'Friday', 'Saturday' or 'Sunday'  | 
|  service.executionWindows[].windowStartHour  |  datetime.time  |  UTC 24-hour format of the time when the execution window starts  | 
|  service.executionWindows[].windowEndHour  |  datetime.time  |  UTC 24-hour format of the time when the execution window ends  | 
|  service.qpu\$1capabilities.service.shotsRange  |  Tuple[int, int]  |  Minimum and maximum number of shots for the device  | 
|  service.qpu\$1capabilities.service.deviceCost.price  |  float  |  Price of the device in terms of US dollars  | 
|  service.qpu\$1capabilities.service.deviceCost.unit  |  str  |  unit for charging the price, e.g: 'minute', 'hour', 'shot', 'task'  | 


**Metadata fields**  

| Metadata field | type | description | 
| --- | --- | --- | 
|  action[].version  |  str  |  version of the AHS program schema  | 
|  action[].actionType  |  ActionType  |  AHS program schema name; must be 'braket.ir.ahs.program'  | 
|  service.braketSchemaHeader.name  |  str  |  name of the schema; must be 'braket.device\$1schema.device\$1service\$1properties'  | 
|  service.braketSchemaHeader.version  |  str  |  version of the schema  | 
|  service.deviceDocumentation.imageUrl  |  str  |  URL for the image of the device  | 
|  service.deviceDocumentation.summary  |  str  |  brief description on the device  | 
|  service.deviceDocumentation.externalDocumentationUrl  |  str  |  external documentation URL  | 
|  service.deviceLocation  |  str  |  geographic location fo the device  | 
|  service.updatedAt  |  datetime  |  time when the device properties were last updated  | 

# Working with AWS Boto3
<a name="braket-using-boto3"></a>

Boto3 is the AWS SDK for Python. With Boto3, Python developers can create, configure, and manage AWS services, such as Amazon Braket. Boto3 provides an object-oriented API, as well as low-level access to Amazon Braket.

Follow the instructions in the [Boto3 Quickstart guide](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html) to learn how to install and configure Boto3.

Boto3 provides the core functionality that works along with the Amazon Braket Python SDK to help you configure and run your quantum tasks. Python customers always need to install Boto3, because that is the core implementation. If you want to make use of additional helper methods, you also need to install the Amazon Braket SDK.

For example, when you call `CreateQuantumTask`, the Amazon Braket SDK submits the request to Boto3, which then calls the AWS API.

**Topics**
+ [Turn on the Amazon Braket Boto3 client](braket-using-boto3-client.md)
+ [Configure AWS CLI profiles for Boto3 and the Braket SDK](braket-using-boto3-profiles.md)

# Turn on the Amazon Braket Boto3 client
<a name="braket-using-boto3-client"></a>

To use Boto3 with Amazon Braket, you must import Boto3 and then define a client that you use to connect to the Amazon Braket API. In the following example, the Boto3 client is named `braket`.

```
import boto3
import botocore

braket = boto3.client("braket")
```

**Note**  
[Braket supports IPv6](https://docs.aws.amazon.com/vpc/latest/userguide/aws-ipv6-support.html). If you are using an IPv6-only network or wish to ensure your workload uses IPv6 traffic, use the dual-stack endpoints as outlined in the [Dual-stack and FIPS endpoints](https://docs.aws.amazon.com/sdkref/latest/guide/feature-endpoints.html) guide.

Now that you have a `braket` client established, you can make requests and process responses from the Amazon Braket service. You can get more detail on request and response data in the [API Reference](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/braket.html).

**Topics**
+ [Search for devices](#braket-using-boto3-example-search-devices)
+ [Retrieve a device](#braket-using-boto3-example-retrieve-devices)
+ [Create a quantum task](#braket-using-boto3-example-create-task)
+ [Retrieve a quantum task](#braket-using-boto3-example-retrieve-task)
+ [Search for quantum tasks](#braket-using-boto3-example-search-tasks)
+ [Cancel quantum task](#braket-using-boto3-example-cancel-task)

## Search for devices
<a name="braket-using-boto3-example-search-devices"></a>
+  `search_devices(**kwargs)` 

Search for devices using the specified filters.

```
# Pass search filters and optional parameters when sending the
# request and capture the response
response = braket.search_devices(filters=[{
    'name': 'deviceArn',
    'values': ['arn:aws:braket:::device/quantum-simulator/amazon/sv1']
}], maxResults=10)

print(f"Found {len(response['devices'])} devices")

for i in range(len(response['devices'])):
    device = response['devices'][i]
    print(device['deviceArn'])
```

## Retrieve a device
<a name="braket-using-boto3-example-retrieve-devices"></a>
+  `get_device(deviceArn)` 

Retrieve the devices available in Amazon Braket.

```
# Pass the device ARN when sending the request and capture the repsonse
response = braket.get_device(deviceArn='arn:aws:braket:::device/quantum-simulator/amazon/sv1')

print(f"Device {response['deviceName']} is {response['deviceStatus']}")
```

## Create a quantum task
<a name="braket-using-boto3-example-create-task"></a>
+  `create_quantum_task(**kwargs)` 

Create a quantum task.

```
# Create parameters to pass into create_quantum_task()
kwargs = {
    # Create a Bell pair
    'action': '{"braketSchemaHeader": {"name": "braket.ir.jaqcd.program", "version": "1"}, "results": [], "basis_rotation_instructions": [], "instructions": [{"type": "h", "target": 0}, {"type": "cnot", "control": 0, "target": 1}]}',
    # Specify the SV1 Device ARN
    'deviceArn': 'arn:aws:braket:::device/quantum-simulator/amazon/sv1',
    # Specify 2 qubits for the Bell pair
    'deviceParameters': '{"braketSchemaHeader": {"name": "braket.device_schema.simulators.gate_model_simulator_device_parameters", "version": "1"}, "paradigmParameters": {"braketSchemaHeader": {"name": "braket.device_schema.gate_model_parameters", "version": "1"}, "qubitCount": 2}}',
    # Specify where results should be placed when the quantum task completes.
    # You must ensure the S3 Bucket exists before calling create_quantum_task()
    'outputS3Bucket': 'amazon-braket-examples',
    'outputS3KeyPrefix': 'boto-examples',
    # Specify number of shots for the quantum task
    'shots': 100
}

# Send the request and capture the response
response = braket.create_quantum_task(**kwargs)

print(f"Quantum task {response['quantumTaskArn']} created")
```

## Retrieve a quantum task
<a name="braket-using-boto3-example-retrieve-task"></a>
+  `get_quantum_task(quantumTaskArn)` 

Retrieve the specified quantum task.

```
# Pass the quantum task ARN when sending the request and capture the response
response = braket.get_quantum_task(quantumTaskArn='arn:aws:braket:us-west-1:123456789012:quantum-task/ce78c429-cef5-45f2-88da-123456789012')

print(response['status'])
```

## Search for quantum tasks
<a name="braket-using-boto3-example-search-tasks"></a>
+  `search_quantum_tasks(**kwargs)` 

Search for quantum tasks that match the specified filter values.

```
# Pass search filters and optional parameters when sending the
# request and capture the response
response = braket.search_quantum_tasks(filters=[{
    'name': 'deviceArn',
    'operator': 'EQUAL',
    'values': ['arn:aws:braket:::device/quantum-simulator/amazon/sv1']
}], maxResults=25)

print(f"Found {len(response['quantumTasks'])} quantum tasks")

for n in range(len(response['quantumTasks'])):
    task = response['quantumTasks'][n]
    print(f"Quantum task {task['quantumTaskArn']} for {task['deviceArn']} is {task['status']}")
```

## Cancel quantum task
<a name="braket-using-boto3-example-cancel-task"></a>
+  `cancel_quantum_task(quantumTaskArn)` 

Cancel the specified quantum task.

```
# Pass the quantum task ARN when sending the request and capture the response
response = braket.cancel_quantum_task(quantumTaskArn='arn:aws:braket:us-west-1:123456789012:quantum-task/ce78c429-cef5-45f2-88da-123456789012')

print(f"Quantum task {response['quantumTaskArn']} is {response['cancellationStatus']}")
```

# Configure AWS CLI profiles for Boto3 and the Braket SDK
<a name="braket-using-boto3-profiles"></a>

The Amazon Braket SDK relies upon the default AWS CLI credentials, unless you explicitly specify otherwise. We recommend that you keep the default when you run on a managed Amazon Braket notebook because you must provide an IAM role that has permissions to launch the notebook instance.

Optionally, if you run your code locally (on an Amazon EC2 instance, for example), you can establish named AWS CLI profiles. You can give each profile a different permission set, rather than regularly overwriting the default profile.

This section provides a brief explanation of how to configure such a CLI `profile` and how to incorporate that profile into Amazon Braket so that API calls are made with the permissions from that profile.

**Topics**
+ [Step 1: Configure a local AWS CLI `profile`](#braket-using-boto3-profiles-step-1)
+ [Step 2: Establish a Boto3 session object](#braket-using-boto3-profiles-step-2)
+ [Step 3: Incorporate the Boto3 session into the Braket AwsSession](#braket-using-boto3-profiles-step-3)

## Step 1: Configure a local AWS CLI `profile`
<a name="braket-using-boto3-profiles-step-1"></a>

It is beyond the scope of this document to explain how to create a user and how to configure a non-default profile. For information on these topics, see:
+  [Getting started](https://docs.aws.amazon.com/singlesignon/latest/userguide/getting-started.html) 
+  [Configuring the AWS CLI to use AWS IAM Identity Center](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html) 

To use Amazon Braket, you must provide this user — and the associated CLI `profile` — with the necessary Braket permissions. For instance, you can attach the **AmazonBraketFullAccess** policy.

## Step 2: Establish a Boto3 session object
<a name="braket-using-boto3-profiles-step-2"></a>

In order to establish a Boto3 session object, utilize the following code example.

```
from boto3 import Session

# Insert CLI profile name here
boto_sess = Session(profile_name=`profile`)
```

**Note**  
If the expected API calls have Region-based restrictions that are not aligned with your `profile` default Region, you can specify a Region for the Boto3 session as shown in the following example.

```
# Insert CLI profile name _and_ region
boto_sess = Session(profile_name=`profile`, region_name=`region`)
```

For the argument designated as `region`, substitute a value that corresponds to one of the AWS Regions in which Amazon Braket is available such as `us-east-1`, `us-west-1`, and so forth.

## Step 3: Incorporate the Boto3 session into the Braket AwsSession
<a name="braket-using-boto3-profiles-step-3"></a>

The following example shows how to initialize a Boto3 Braket session and instantiate a device in that session.

```
from braket.aws import AwsSession, AwsDevice

# Initialize Braket session with Boto3 Session credentials
aws_session = AwsSession(boto_session=boto_sess)

# Instantiate any Braket QPU device with the previously initiated AwsSession
sim_arn = 'arn:aws:braket:::device/quantum-simulator/amazon/sv1'
device = AwsDevice(sim_arn, aws_session=aws_session)
```

After this setup is complete, you can submit quantum tasks to that instantiated `AwsDevice` object (by calling the `device.run(…​)` command for example). All API calls made by that device can use the IAM credentials associated with the CLI profile that you previously designated as `profile`.