Skip to main content

Amazon Braket

The Strangeworks Platform supports Amazon Braket, a fully managed quantum computing service that helps you get started with quantum computing. Amazon Braket provides a development environment for you to explore and build quantum algorithms, test them on quantum circuit simulators, and run them on different quantum hardware technologies.

strangeworks-braketā€‹

The strangeworks-braket SDK provides a drop-in replacement for code written for Amazon Braket.

PyPI version

šŸ“‘ Package Documentation

Installationā€‹

To get started, make sure you have Python 3.10 or 3.11 (installation) and are familiar with setting up and using virtual environments.

pip install -U pip && pip install strangeworks-braket

Authenticationā€‹

First, authenticate via the Strangeworks SDK with your api-key taken from the Portal homepage:

import strangeworks as sw
from strangeworks_braket import StrangeworksDevice
sw.authenticate(api_key)

Backendsā€‹

List Available Devices and Display Device ARN
devices = StrangeworksDevice.get_devices(statuses=["ONLINE"])
print("Available devices:")
for device in devices:
print(f" - {device.name} ({device.arn})")
CompanyProcessorQubitsFrameworkProcessors
QuEraAquila256Braketbraket.aquila, arn:aws:braket:us-east-1::device/qpu/quera/Aquila
IonQAria25Braketarn:aws:braket:us-east-1::device/qpu/ionq/Aria-1, arn:aws:braket:us-east-1::device/qpu/ionq/Aria-2
IonQForte 125Braketarn:aws:braket:us-east-1::device/qpu/ionq/Forte-1
IQMGarnet20Braketarn:aws:braket:eu-north-1::device/qpu/iqm/Garnet

Disclaimer: This list of backends is subject to change at any time.

For more accurate and up-to-date information, please refer to the Amazon Braket documentation

Tasksā€‹

Run a Taskā€‹

Amazon Braket: Hello World
import strangeworks as sw
from braket.circuits import Circuit
from strangeworks_braket import StrangeworksDevice

sw.authenticate(api_key)

# create a simple quantum circuit
bell_state = Circuit().h(0).cnot(0, 1)

# Choose a device (an AWS-hosted simulator in this case)
tn1 = StrangeworksDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")

# Execute the circuit
print("\nšŸ¤– Executing Circuit...\n")

task = tn1.run(bell_state, 1000)

# Add a list of tags to the task to help with organization
sw.add_tags(task.id, ["bell-state"])

# At this point, the job is running on the Strangeworks Platform.
# You can check the status of the job in the Portal.
print(f"ā³ Job {task.id} submitted!\n")

# Lots of good info in here
result = task.result()

# View the counts (also visible in the Portal in a chart šŸ“Š)
print(f"šŸŽ‰ šŸ“Š Counts: {result.measurement_counts}\n")

šŸ„³ Success! You may view your job in the portal.

šŸ˜… Something went wrong? Find us in Slack!

Get a Taskā€‹

from strangeworks_braket import StrangeworksQuantumTask

task = StrangeworksQuantumTask.from_strangeworks_slug(task_slug)
result = task.result()

Cancel a Taskā€‹

You can cancel a task using the cancel method. This will stop the task and prevent further charges although you may still be charged for the time the task was running.

Amazon Braket: Cancel Job
from strangeworks_braket import StrangeworksQuantumTask

task = StrangeworksQuantumTask.from_strangeworks_slug(task_slug)

# Cancel the job
task.cancel()

Hybrid Jobsā€‹

Hybrid jobs allow for the integration of classical and quantum computing tasks. The execution of these tasks on the Strangeworks Platform is similar to Braket Hybrid Jobs.

The StrangeworksQuantumJob.create() method offers a user-friendly alternative to AwsQuantumJob.create(), streamlining the job submission process by focusing on essential parameters. This makes it easier for users to create jobs on the Strangeworks platform, with the source module path and hyperparameters passed directly as arguments.

note

While this simplification enhances usability, it is important to note that some advanced AWS-specific features, such as specifying input data or controlling job completion waiting, are not directly exposed.

Job Scriptā€‹

note

To ensure your script runs correctly, follow these guidelines:

  • Define a single main function with no inputs: def main(). Use load_hyperparameters() to read inputs within this function.
  • Import the Braket tracker: from braket.tracking import Tracker.
  • Start the tracker immediately after def main(): or any comments following it.
  • Import the save job result function: from braket.jobs import save_job_result.
  • Save the tracker's results using save_job_result with "task summary": t.quantum_tasks_statistics(), in the output dictionary.
  • Ensure save_job_result is the last line in the main function.

An example script is shown below which will run a simple bell state circuit a number of times given by the input parameter num_iter. To run the example, first save or download the braket_hybrid_script.py

Amazon Braket: Hybrid Job Script

import json
import os

from braket.aws import AwsDevice
from braket.circuits import Circuit
from braket.jobs import save_job_result
from braket.tracking import Tracker


def main():
"""
Example Hybrid Job File
"""
t = Tracker().start()

device_arn = os.environ["AMZN_BRAKET_DEVICE_ARN"]
# Initialise device
device = AwsDevice(device_arn)
print(f"Using device {device}")

hyperparams = load_hyperparameters()
print("Hyperparameters are:", hyperparams)

shots = int(hyperparams.get("shots"))
num_iter = int(hyperparams.get("num_iter"))

counts = []
bell = Circuit().h(0).cnot(0, 1)
for count in range(num_iter):
task = device.run(bell, shots=shots)
print(task.result().measurement_counts)
counts.append(task.result().measurement_counts)

# return results as strings
save_job_result(
{
"counts": json.dumps(counts),
"task summary": t.quantum_tasks_statistics(),
"estimated cost": t.qpu_tasks_cost() + t.simulator_tasks_cost()
}
)


def load_hyperparameters():
"""Load the Hybrid Job hyperparameters"""
hp_file = os.environ["AMZN_BRAKET_HP_FILE"]
with open(hp_file) as f:
hyperparams = json.load(f)
return hyperparams

Run a Jobā€‹

StrangeworksQuantumJob.create() takes:

  • source_module: The file containing the code to run.
  • hyperparameters: A dictionary of hyperparameters.
  • device: The ARN of the device to run the job on.
  • job_name: The name of the job, added as a tag.
note

StrangeworksQuantumJob.create() does not take:

  • entry_point (always uses source_module::main)
  • input_data
  • wait_until_complete

Hereā€™s how the script is implemented using the Strangeworks SDK.

Strangeworks Braket Service: Hybrid Jobs
import strangeworks as sw
from strangeworks_braket import StrangeworksQuantumJob

# Authenticate with your Strangeworks API key
sw.authenticate(api_key)

sw_job = StrangeworksQuantumJob.create(
source_module="./braket_hybrid_script.py",
hyperparameters={
"shots": 100,
"num_iter": 5
},
device="arn:aws:braket:::device/quantum-simulator/amazon/sv1",
job_name="Bell State Experiment"
)

# Add a list of tags to the job to help with organization
sw.add_tags(sw_job.id, ["quantum-computing"])

# Retrieve the job result
result = sw_job.result()
print(result)

šŸ„³ Success! You may view your job in the portal.

šŸ˜… Something went wrong? Find us in Slack!

Get a Jobā€‹

from strangeworks_braket import StrangeworksQuantumJob

job = StrangeworksQuantumJob.from_strangeworks_slug(job_slug)
result = job.result()

Cancel a Jobā€‹

For hybrid jobs, the cancel command terminates the classical hybrid job container immediately and does a best effort to cancel all of the related quantum tasks that are still in a non-terminal state. This will stop the job and prevent further charges although some charges may still apply for the work that has already been done. The job status will be updated to CANCELLED in the portal.

Amazon Braket: Cancel Hybrid Job
from strangeworks_braket import StrangeworksQuantumJob

job = StrangeworksQuantumJob.from_strangeworks_slug(job_slug)

# Cancel the job
job.cancel()

Advanced Featuresā€‹

Error Mitigationā€‹

Error mitigation techniques on IonQ Aria

Strangeworks Braket: Error Mitigation
import strangeworks as sw
from strangeworks_braket import StrangeworksDevice
from braket.circuits import Circuit
from braket.error_mitigation import Debias

sw.authenticate(api_key)
device = StrangeworksDevice("ionq_aria")
circuit = Circuit().h(0).cnot(0, 1)

task = device.run(circuit, shots=2500, device_parameters={"errorMitigation": Debias()})

sw.add_tags(task.id, ["debias"])

result = task.result()
print(result.measurement_counts)

šŸ„³ Success! You may view your job in the portal.

šŸ˜… Something went wrong? Find us in Slack!

Pulse Controlā€‹

Pulse control on Braket is supported by using StrangeworksDevice

Qubit Spectroscopyā€‹

The following example demonstrates how to perform qubit spectroscopy using a Gaussian waveform.

Qubit Spectroscopy
import strangeworks as sw
from strangeworks_braket import StrangeworksDevice
from braket.pulse import GaussianWaveform, PulseSequence
from braket.parametric import FreeParameter

sw.authenticate(api_key)
device = StrangeworksDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-2")

if device.status == "ONLINE":
device_name = "ankaa"

experiment_configuration = {
"ankaa": {
"device_arn": "arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-2",
"qubit": 3,
"drive_frame": "Transmon_3_charge_tx",
"readout_frame": "Transmon_3_readout_rx",
"spectroscopy_wf": GaussianWaveform(100e-9, 25e-9, 0.1, True),
"rabi_wf": GaussianWaveform(
FreeParameter("length"), FreeParameter("length") * 0.25, 0.2, True
),
},
}

drive_frame = device.frames[experiment_configuration[device_name]["drive_frame"]]
readout_frame = device.frames[experiment_configuration[device_name]["readout_frame"]]
waveform = experiment_configuration[device_name]["spectroscopy_wf"]

frequency = FreeParameter("frequency")

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

span = 75e6
N_shots = 100

qubit_spectroscopy_sequences = pulse_sequence(frequency=drive_frame.frequency - span / 2)
task = device.run(qubit_spectroscopy_sequences, shots=N_shots)

sw.add_tags(task.id, ["qubit-spectroscopy"])

print(task.result().measurement_counts)

Custom Gate with Pulseā€‹

The following example demonstrates how to create a custom single-qubit gate using Gaussian waveforms.

Custom Gate with Pulse
import strangeworks as sw
from strangeworks_braket import StrangeworksDevice
import braket.circuits.circuit as circuit
from braket.pulse import GaussianWaveform, PulseSequence
from braket.parametric import FreeParameter
from braket.circuits import Circuit

sw.authenticate(api_key)
device = StrangeworksDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-2")

if device.status == "ONLINE":
qubit = 3
drive_frame = device.frames[f"Transmon_{qubit}_charge_tx"]

width = 5e-9
length = 40e-9

x90_amplitude = 0.9
x90 = GaussianWaveform(length, width, x90_amplitude, False)

lambda_ = FreeParameter("lambda_")
theta = FreeParameter("theta")
phi = FreeParameter("phi")

U_sequence = (
PulseSequence()
.shift_phase(drive_frame, np.pi / 2 - lambda_)
.play(drive_frame, x90)
.shift_phase(drive_frame, theta - np.pi)
.play(drive_frame, x90)
.shift_phase(drive_frame, np.pi / 2 - phi)
)

@circuit.subroutine(register=True)
def U_pulses(theta, phi, lambda_):
return Circuit().pulse_gate(
[qubit],
pulse_sequence=U_sequence(theta=theta, phi=phi, lambda_=lambda_),
)

nb_shots = 500
task = device.run(
Circuit().rx(4, np.pi / 2).rz(4, np.pi / 2).U_pulses(2 * np.pi, 0, 0),
shots=nb_shots,
disable_qubit_rewiring=True,
)

sw.add_tags(task.id, ["custom-gate"])

print(task.result().measurement_counts)

šŸ„³ Success! You may view your job in the portal.

šŸ˜… Something went wrong? Find us in Slack!

PennyLane with Hybrid Jobsā€‹

Hybrid jobs can be seamlessly integrated with PennyLane, a versatile library for quantum machine learning, quantum computing, and quantum chemistry. For detailed guidance on running hybrid jobs with PennyLane, including the QAOA algorithm, please refer to the Amazon Braket documentation.

note

Please note that the Amazon Braket PennyLane plugin is not supported for use within local notebooks on the Strangeworks Platform.

Example Scriptā€‹

Here is pennylane_hybridjob.py an example script that integrates PennyLane:

Pennylane Hybrid Job Script
import os
import json
import time

import networkx as nx
import pennylane as qml
from pennylane import numpy as np
from braket.jobs import save_job_result
from braket.jobs.metrics import log_metric
from braket.tracking import Tracker

def init_pl_device(device_arn, num_nodes, max_parallel):
device_prefix = device_arn.split(":")[0]

if device_prefix == "local":
prefix, device_name = device_arn.split("/")
print("Using local simulator: ", device_name)
device = qml.device(
device_name,
wires=num_nodes,
custom_decomps={"MultiRZ": qml.MultiRZ.compute_decomposition}
)
else:
print("Using AWS managed device: ", device_arn)
device = qml.device(
"braket.aws.qubit",
device_arn=device_arn,
wires=num_nodes,
parallel=True,
max_parallel=max_parallel
)

return device

def main():
"""
Example Hybrid Job File
"""
# Start tracking the job
t = Tracker().start()

# Read environment variables
hp_file = os.environ["AMZN_BRAKET_HP_FILE"]
device_arn = os.environ["AMZN_BRAKET_DEVICE_ARN"]

# Load hyperparameters
with open(hp_file, "r") as f:
hyperparams = json.load(f)
print("Hyperparameters: ", hyperparams)

# Problem setup hyperparameters
n_nodes = int(hyperparams["n_nodes"])
n_edges = float(hyperparams["n_edges"])
n_layers = int(hyperparams["n_layers"])

# Training hyperparameters
seed = int(hyperparams["seed"])
iterations = int(hyperparams["iterations"])
stepsize = float(hyperparams["stepsize"])
diff_method = hyperparams["diff_method"]
max_parallel = int(hyperparams["max_parallel"])

# Initialize the device
device = init_pl_device(device_arn, n_nodes, max_parallel)

# Set up the graph
g = nx.gnm_random_graph(n_nodes, n_edges, seed=seed)

# Define the QAOA problem
cost_h, mixer_h = qml.qaoa.max_clique(g, constrained=False)
print("Number of observables: ", len(cost_h._ops))

def qaoa_layer(gamma, alpha):
qml.qaoa.cost_layer(gamma, cost_h)
qml.qaoa.mixer_layer(alpha, mixer_h)

def circuit(params):
for i in range(n_nodes):
qml.Hadamard(wires=i)
qml.layer(qaoa_layer, n_layers, params[0], params[1])

@qml.qnode(device, diff_method=diff_method)
def cost_function(params):
circuit(params)
return qml.expval(cost_h)

# Optimization process
print("Starting optimization...")
np.random.seed(seed)
params = np.random.uniform(size=[2, n_layers])

opt = qml.AdamOptimizer(stepsize=stepsize)

for i in range(iterations):
t0 = time.time()

# Perform a gradient step
params, cost_before = opt.step_and_cost(cost_function, params)
cost_before = float(cost_before)

t1 = time.time()

if i == 0:
print("Initial cost:", cost_before)
else:
print(f"Cost at step {i}:", cost_before)

# Log the current loss as a metric
log_metric(
metric_name="Cost",
value=cost_before,
iteration_number=i,
)

print(f"Completed iteration {i + 1}")
print(f"Time to complete iteration: {t1 - t0} seconds")

final_cost = float(cost_function(params))
log_metric(
metric_name="Cost",
value=final_cost,
iteration_number=iterations,
)

# Save the job result
save_job_result(
{
"params": params.tolist(),
"cost": final_cost,
"task summary": t.quantum_tasks_statistics(),
"estimated cost": t.qpu_tasks_cost() + t.simulator_tasks_cost(),
}
)

Run the Scriptā€‹

To execute the script, you can use the following command:

Strangeworks Braket: Hybrid Jobs with PennyLane

import strangeworks as sw
from strangeworks_braket import StrangeworksQuantumJob

sw.authenticate(api_key)

sw_job = StrangeworksQuantumJob.create(
source_module="./pennylane_hybridjob.py",
hyperparameters = {
"n_nodes": 6,
"n_edges": 10,
"n_layers": 3,
"iterations": 10,
"stepsize": 0.1,
"seed": 42,
"diff_method": "parameter-shift",
"max_parallel": 30
},
device="arn:aws:braket:::device/quantum-simulator/amazon/sv1",
job_name="Pennylane QAOA"
)

# Retrieve the job result
result = sw_job.result()
print(result)