Skip to main content

Amazon Braket

☁️

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.

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

note

Pricing is based on the number of quantum tasks executed.

pip install -U pip strangeworks-braket

Package Documentation 📖

PyPI version PyPI - Python Version

🔑

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})")
Loading quantum backends...

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

🦾

Quickstart

Hello World

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? Contact us at support@strangeworks.com for assistance.

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

danger

Do NOT run this script directly! This script is uploaded to the Strangeworks Platform and executed remotely. You should only save this code as a file (e.g., braket_hybrid_script.py) and reference it in the submission code shown in the "Run a Job" section below.

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. Save this code as a file (e.g., braket_hybrid_script.py) - you can also download it from braket_hybrid_script.py. This file will be uploaded to the platform when you submit the job using the code in the next section.

Amazon Braket: Hybrid Job Script
# WARNING: Do NOT run this script directly!
# This script is uploaded to the Strangeworks Platform and executed remotely.
# Save this code as a file (e.g., braket_hybrid_script.py) and reference it
# in the submission code shown in the "Run a Job" section.

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

tip

This is the code you should run! Execute this code in your local Python environment to submit the hybrid job. Make sure you've saved the job script from the previous section as a file (e.g., braket_hybrid_script.py) in the same directory.

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 to submit the hybrid job using the Strangeworks SDK. Run this code in your local environment:

Strangeworks Braket Service: Hybrid Jobs
# This is the code you should run in your local Python environment!
# Make sure you've saved the job script (braket_hybrid_script.py) in the same directory.

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",
input_data={}, # No input data needed for this example
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? Contact us at support@strangeworks.com for assistance.

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()

Input Data

Hybrid jobs can also accept input data files that are made available to your job script. This is useful for providing datasets, configuration files, or other resources that your quantum algorithm needs.

Job Script with Input Data

danger

Do NOT run this script directly! This script is uploaded to the Strangeworks Platform and executed remotely. You should only save this code as a file and reference it in the submission code shown in the "Run a Job with Input Data" section below.

Here's an example script that demonstrates how to load and use input data files. Save this code as a file (e.g., hybrid_job_with_input_data.py):

Hybrid Job Script with Input Data
# WARNING: Do NOT run this script directly!
# This script is uploaded to the Strangeworks Platform and executed remotely.
# Save this code as a file (e.g., hybrid_job_with_input_data.py) and reference it
# in the submission code shown in the "Run a Job with Input Data" section.

import json
import os
import pandas as pd
import numpy as np

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 with Input Data
"""
t = Tracker().start()

# Load input data from the environment variable
input_dir = os.environ["AMZN_BRAKET_INPUT_DIR"]

# Load CSV data
flight_df = pd.read_csv(f"{input_dir}/flight-data/flight_data.csv")
flight_ids = flight_df.index.tolist()
capacity = {idx: row["Plane Capacity"] for idx, row in flight_df.iterrows()}

# Load JSON data
with open(f"{input_dir}/quantum-params/quantum_parameters.json", "r") as f:
quantum_data = json.load(f)

device_arn = os.environ["AMZN_BRAKET_DEVICE_ARN"]
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"))

# Use the input data in your quantum algorithm
print(f"Loaded {len(flight_df)} flight records from CSV")
print(f"Loaded quantum parameters: {list(quantum_data.keys())}")

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)

# Save results including input data summary
save_job_result(
{
"counts": json.dumps(counts),
"input_data_summary": {
"flight_records": len(flight_df),
"quantum_params": list(quantum_data.keys()),
"sample_capacity": list(capacity.values())
},
"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 with Input Data

tip

This is the code you should run! Execute this code in your local Python environment to submit the hybrid job with input data. Make sure you've saved the job script from the previous section as a file in the same directory.

To submit a hybrid job with input data, use the input_data parameter in StrangeworksQuantumJob.create(). Run this code in your local environment:

Strangeworks Braket: Hybrid Jobs with Input Data
# This is the code you should run in your local Python environment!
# Make sure you've saved the job script (hybrid_job_with_input_data.py) in the same directory.

import strangeworks as sw
from strangeworks_braket import StrangeworksQuantumJob

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

# Create input data files (these would typically be your actual data files)
# Example: quantum_parameters.json
quantum_params = {
"rotation_angles": [0.048, 0.265, 0.462, 0.905, 1.276, 1.534, 0.730, 0.226, 1.439, 0.566],
"gate_sequence": [0.084, 0.254, 0.414, 1.078, 0.801, 1.487, 0.213, 0.575, 0.510, 0.353],
"optimization_weights": [1.093, 1.025, 1.483, 0.350, 1.435, 0.095, 0.833, 0.570, 0.604, 1.031]
}
import json
with open("quantum_parameters.json", "w") as f:
json.dump(quantum_params, f)

# Example: flight_data.csv
import pandas as pd
flight_data = {
"Plane Capacity": [5000, 7000, 10000],
"Cost of Transport Per Cargo": [15, 30, 45]
}
df = pd.DataFrame(flight_data)
df.to_csv("flight_data.csv", index=False)

# Submit the hybrid job with input data
sw_job = StrangeworksQuantumJob.create(
source_module="./hybrid_job_with_input_data.py",
input_data={
"quantum-params": "quantum_parameters.json",
"flight-data": "flight_data.csv"
},
hyperparameters={
"shots": 100,
"num_iter": 5
},
device="arn:aws:braket:::device/quantum-simulator/amazon/sv1",
job_name="Hybrid Job with Input Data"
)

# Add tags for organization
sw.add_tags(sw_job.id, ["hybrid-job", "input-data", "data-processing"])

print(f"⏳ Job submitted with ID: {sw_job.id}")

# Retrieve the job result
result = sw_job.result()
print("📊 Job Results:")
print(f"Quantum counts: {result['counts']}")
print(f"Input data summary: {result['input_data_summary']}")

Key Points for Input Data

  • File Access: Input data files are available in the AMZN_BRAKET_INPUT_DIR environment variable
  • File Structure: Files are organized in subdirectories based on the keys you provide in input_data
  • Supported Formats: CSV, JSON, text files, and other data formats
  • File Size Limits: Be mindful of file size limits for your input data
  • Data Processing: Use the input data to parameterize your quantum algorithms or provide classical data for hybrid approaches

🥳 Success! You may view your job in the portal.

😅 Something went wrong? Contact us at support@strangeworks.com for assistance.

PennyLane

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

danger

Do NOT run this script directly! This script is uploaded to the Strangeworks Platform and executed remotely. You should only save this code as a file and reference it in the submission code shown in the "Run the Script" section below.

Here is an example script that integrates PennyLane. Save this code as a file (e.g., pennylane_hybridjob.py) - you can also download it from pennylane_hybridjob.py:

Pennylane Hybrid Job Script
# WARNING: Do NOT run this script directly!
# This script is uploaded to the Strangeworks Platform and executed remotely.
# Save this code as a file (e.g., pennylane_hybridjob.py) and reference it
# in the submission code shown in the "Run the Script" section.

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

tip

This is the code you should run! Execute this code in your local Python environment to submit the PennyLane hybrid job. Make sure you've saved the job script from the previous section as a file (e.g., pennylane_hybridjob.py) in the same directory.

To submit the PennyLane hybrid job, run this code in your local environment:

Strangeworks Braket: Hybrid Jobs with PennyLane
# This is the code you should run in your local Python environment!
# Make sure you've saved the job script (pennylane_hybridjob.py) in the same directory.

import strangeworks as sw
from strangeworks_braket import StrangeworksQuantumJob

sw.authenticate(api_key)

sw_job = StrangeworksQuantumJob.create(
source_module="./pennylane_hybridjob.py",
input_data={}, # No input data needed for this example
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)

QAOA for Max-Cut

This example demonstrates how to implement the Quantum Approximate Optimization Algorithm (QAOA) for solving the Max-Cut problem using Strangeworks Hybrid Jobs. The algorithm combines classical optimization with quantum circuits to find approximate solutions to combinatorial optimization problems.

The QAOA approach uses a parameterized quantum circuit that is optimized classically to minimize a cost function. This example implements the Max-Cut problem, which seeks to partition graph vertices into two sets to maximize the number of edges between them.

danger

Do NOT run this script directly! This script is uploaded to the Strangeworks Platform and executed remotely. You should only save this code as a file and reference it in the submission code shown in the next section below.

First, save the following script as a file (e.g., qaoa_maxcut_hybrid.py) - you can also download it from qaoa_maxcut_hybrid.py:

QAOA Max-Cut Hybrid Job Script
# WARNING: Do NOT run this script directly!
# This script is uploaded to the Strangeworks Platform and executed remotely.
# Save this code as a file (e.g., qaoa_maxcut_hybrid.py) and reference it
# in the submission code shown in the next section.

import json
import os
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 create_maxcut_graph(n_nodes, n_edges, seed):
"""Create a random graph for the Max-Cut problem."""
np.random.seed(seed)
graph = nx.gnm_random_graph(n_nodes, n_edges, seed=seed)
return graph


def maxcut_cost_hamiltonian(graph):
"""Generate the cost Hamiltonian for Max-Cut problem."""
coeffs = []
obs = []

for edge in graph.edges():
coeffs.append(0.5)
obs.append(qml.PauliZ(edge[0]) @ qml.PauliZ(edge[1]))

# Add constant term (number of edges / 2)
for i in range(len(coeffs)):
coeffs[i] = -coeffs[i] # Minimize instead of maximize

return qml.Hamiltonian(coeffs, obs)


def mixer_hamiltonian(n_qubits):
"""Generate the mixer Hamiltonian (sum of X gates)."""
coeffs = [1.0] * n_qubits
obs = [qml.PauliX(i) for i in range(n_qubits)]
return qml.Hamiltonian(coeffs, obs)


def qaoa_circuit(params, cost_ham, mixer_ham, n_layers):
"""QAOA circuit with alternating cost and mixer layers."""
gammas, betas = params

# Initial superposition
for i in range(len(mixer_ham.ops)):
qml.Hadamard(wires=i)

# QAOA layers
for layer in range(n_layers):
# Cost layer
qml.templates.ApproxTimeEvolution(cost_ham, gammas[layer], 1)
# Mixer layer
qml.templates.ApproxTimeEvolution(mixer_ham, betas[layer], 1)


def init_device(device_arn, n_qubits):
"""Initialize PennyLane device for the given device ARN."""
if device_arn.startswith("local"):
device = qml.device("default.qubit", wires=n_qubits)
else:
device = qml.device(
"braket.aws.qubit",
device_arn=device_arn,
wires=n_qubits,
shots=1000
)
return device


def main():
"""Main QAOA optimization function."""
# Start tracking
t = Tracker().start()

# Load hyperparameters
hp_file = os.environ["AMZN_BRAKET_HP_FILE"]
device_arn = os.environ.get("AMZN_BRAKET_DEVICE_ARN", "local/default.qubit")

with open(hp_file, "r") as f:
hyperparams = json.load(f)

print("Hyperparameters:", hyperparams)

# Extract hyperparameters
n_nodes = int(hyperparams["n_nodes"])
n_edges = int(hyperparams["n_edges"])
n_layers = int(hyperparams["n_layers"])
n_iterations = int(hyperparams["n_iterations"])
learning_rate = float(hyperparams["learning_rate"])
seed = int(hyperparams.get("seed", 42))

print(f"Solving Max-Cut on {n_nodes} nodes, {n_edges} edges with {n_layers} QAOA layers")

# Create the graph
graph = create_maxcut_graph(n_nodes, n_edges, seed)
print(f"Created graph with {graph.number_of_edges()} edges")

# Set up Hamiltonians
cost_ham = maxcut_cost_hamiltonian(graph)
mixer_ham = mixer_hamiltonian(n_nodes)

# Initialize device
device = init_device(device_arn, n_nodes)

# Create QNode
@qml.qnode(device)
def cost_function(params):
qaoa_circuit(params, cost_ham, mixer_ham, n_layers)
return qml.expval(cost_ham)

# Initialize parameters
np.random.seed(seed)
initial_gammas = np.random.uniform(0, 2*np.pi, n_layers)
initial_betas = np.random.uniform(0, np.pi, n_layers)
params = [initial_gammas, initial_betas]

# Classical optimizer
optimizer = qml.AdamOptimizer(stepsize=learning_rate)

print("Starting QAOA optimization...")
costs = []

for iteration in range(n_iterations):
start_time = time.time()

# Optimization step
params, cost = optimizer.step_and_cost(cost_function, params)
cost = float(cost)
costs.append(cost)

end_time = time.time()
iteration_time = end_time - start_time

print(f"Iteration {iteration + 1}/{n_iterations}: Cost = {cost:.4f}, Time = {iteration_time:.2f}s")

# Log metrics
log_metric(
metric_name="Cost",
value=cost,
iteration_number=iteration
)

log_metric(
metric_name="IterationTime",
value=iteration_time,
iteration_number=iteration
)

# Final evaluation
final_cost = float(cost_function(params))
print(f"Final optimized cost: {final_cost:.4f}")

# Calculate the classical solution for comparison
classical_cut = max_cut_classical(graph)
print(f"Classical max-cut value: {classical_cut}")

# Save results
save_job_result({
"final_cost": final_cost,
"optimal_params": {
"gammas": params[0].tolist(),
"betas": params[1].tolist()
},
"cost_history": costs,
"classical_max_cut": classical_cut,
"graph_info": {
"n_nodes": n_nodes,
"n_edges": graph.number_of_edges(),
"edges": list(graph.edges())
},
"task summary": t.quantum_tasks_statistics(),
"estimated_cost": t.qpu_tasks_cost() + t.simulator_tasks_cost()
})


def max_cut_classical(graph):
"""Calculate classical max-cut using brute force (for small graphs)."""
if graph.number_of_nodes() > 10:
return "Graph too large for brute force"

max_cut = 0
n_nodes = graph.number_of_nodes()

# Try all possible cuts (2^n possibilities)
for i in range(2**n_nodes):
cut_set = []
for j in range(n_nodes):
if (i >> j) & 1:
cut_set.append(j)

# Count edges between the two sets
cut_value = 0
for edge in graph.edges():
if (edge[0] in cut_set) != (edge[1] in cut_set):
cut_value += 1

max_cut = max(max_cut, cut_value)

return max_cut


if __name__ == "__main__":
main()

Now run the QAOA Max-Cut optimization:

tip

This is the code you should run! Execute this code in your local Python environment to submit the QAOA Max-Cut hybrid job. Make sure you've saved the job script from the previous section as a file (e.g., qaoa_maxcut_hybrid.py) in the same directory.

Strangeworks QAOA Max-Cut Hybrid Job
# This is the code you should run in your local Python environment!
# Make sure you've saved the job script (qaoa_maxcut_hybrid.py) in the same directory.

import strangeworks as sw
from strangeworks_braket import StrangeworksQuantumJob

# Authenticate with Strangeworks
sw.authenticate(api_key)

# Submit the QAOA hybrid job
sw_job = StrangeworksQuantumJob.create(
source_module="./qaoa_maxcut_hybrid.py",
input_data={}, # No input data needed for this example
hyperparameters={
"n_nodes": 6, # Number of graph nodes
"n_edges": 8, # Number of graph edges
"n_layers": 2, # Number of QAOA layers (p parameter)
"n_iterations": 50, # Classical optimization iterations
"learning_rate": 0.1, # Adam optimizer learning rate
"seed": 42 # Random seed for reproducibility
},
device="arn:aws:braket:::device/quantum-simulator/amazon/sv1",
job_name="QAOA Max-Cut Optimization"
)

# Add tags for organization
sw.add_tags(sw_job.id, ["qaoa", "max-cut", "optimization", "hybrid-algorithm"])

print(f"⏳ QAOA job submitted with ID: {sw_job.id}")
print("🔗 View progress at: https://portal.strangeworks.com")

# Get results when complete
result = sw_job.result()
print("📊 QAOA Results:")
print(f"Final cost: {result['final_cost']}")
print(f"Classical benchmark: {result['classical_max_cut']}")
print(f"Optimal parameters: {result['optimal_params']}")

This example demonstrates:

  • Graph Generation: Creates a random graph for the Max-Cut problem
  • Hamiltonian Construction: Builds cost and mixer Hamiltonians for QAOA
  • Quantum Circuit: Implements the QAOA ansatz with parameterized layers
  • Classical Optimization: Uses Adam optimizer to minimize the cost function
  • Performance Tracking: Logs metrics and compares against classical solutions
  • Comprehensive Results: Saves optimization history, parameters, and performance metrics

The algorithm iteratively optimizes the quantum circuit parameters to find cuts that maximize the number of edges between graph partitions, showcasing the hybrid quantum-classical approach fundamental to QAOA.

🥳 Success! You may view your job in the portal.

😅 Something went wrong? Contact us at support@strangeworks.com for assistance.

𝌙

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? Contact us at support@strangeworks.com for assistance.

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? Contact us at support@strangeworks.com for assistance.

Mid-Circuit Measurement

Mid-circuit measurement is a powerful feature that allows you to perform measurements during the execution of a quantum circuit and use the results to conditionally apply subsequent operations. This capability is available on certain quantum devices through Amazon Braket's experimental features.

For detailed information about mid-circuit measurement capabilities, limitations, and supported devices, refer to the AWS Quantum Computing Blog.

Key Features and Limitations

  • Supported Devices: Currently available on IQM Garnet and other select devices
  • Conditional Operations: Use measurement results to control subsequent quantum operations
  • Feedback Loops: Enable dynamic circuit execution based on intermediate results
  • Experimental Feature: Requires enabling experimental capabilities

Example: Mid-Circuit Measurement with IQM Garnet

This example demonstrates how to create a simple circuit that uses mid-circuit measurement to conditionally apply operations. The circuit will:

  1. Prepare a qubit in the |1⟩ state
  2. Measure it mid-circuit
  3. Conditionally flip it back to |0⟩ if the measurement result was 1
Mid-Circuit Measurement Example
import strangeworks as sw
from strangeworks_braket import StrangeworksDevice
from braket.circuits import Circuit
from braket.experimental_capabilities import EnableExperimentalCapability
from math import pi

# Authenticate with Strangeworks
sw.authenticate(api_key)

# Use IQM Garnet device for mid-circuit measurement
device = StrangeworksDevice("arn:aws:braket:eu-north-1::device/qpu/iqm/Garnet")

if device.status == "ONLINE":
print("🤖 Creating circuit with mid-circuit measurement...")

# Enable experimental capabilities for mid-circuit measurement
with EnableExperimentalCapability():
circuit = Circuit()

# Step 1: Prepare qubit 1 in the |1⟩ state using a π rotation
circuit.prx(1, pi, 0)

# Step 2: Measure qubit 1 mid-circuit and store result with feedback key 0
circuit.measure_ff(1, feedback_key=0)

# Step 3: Conditionally apply a π rotation if the measurement was 1
# This effectively flips the qubit back to |0⟩ if it was measured as |1⟩
circuit.cc_prx(1, pi, 0, feedback_key=0)

# Wrap the circuit in a verbatim box to preserve the dynamic structure
circuit = Circuit().add_verbatim_box(circuit)

print("🚀 Executing circuit...")

# Execute the circuit with 100 shots
task = device.run(circuit, shots=100)

# Add tags for organization
sw.add_tags(task.id, ["mid-circuit-measurement", "tutorial"])

print(f"⏳ Task submitted with ID: {task.id}")

# Get results
result = task.result()
print(f"📊 Final measurement counts: {result.measurement_counts}")

# The result should show mostly |0⟩ states because:
# - We prepare |1⟩, measure it, then flip it back to |0⟩ if it was |1⟩
# - Any |1⟩ states in the final result are due to quantum noise
print("✅ Mid-circuit measurement completed successfully!")

else:
print("❌ Device is not available. Please try again later.")

Understanding the Circuit Components

  • prx(qubit, angle, phase): Applies a rotation gate around the X-axis
  • measure_ff(qubit, feedback_key): Performs a mid-circuit measurement and stores the result
  • cc_prx(qubit, angle, phase, feedback_key): Applies a conditional rotation based on the measurement result
  • add_verbatim_box(): Preserves the dynamic circuit structure for the quantum device

Use Cases

Mid-circuit measurement enables various advanced quantum algorithms:

  • Quantum Error Correction: Detect and correct errors during computation
  • Adaptive Algorithms: Modify circuit execution based on intermediate results
  • Quantum Machine Learning: Implement feedback mechanisms in quantum neural networks
  • Quantum Control: Create dynamic control sequences for quantum systems
note

Mid-circuit measurement is an experimental feature and may have limitations depending on the specific quantum device. Always check the device documentation for current capabilities and restrictions.

Qiskit Integration

While it's generally recommended to write circuits directly in Braket syntax for full compatibility and feature support, you can quickly convert existing Qiskit circuits to work with Amazon Braket using the qiskit-braket-provider package.

danger

Important Limitations: Not all Qiskit circuits will be supported when converted to Braket. Complex quantum operations, custom gates, or advanced Qiskit features may not translate properly. For production use, consider rewriting circuits directly in Braket syntax for better reliability and full feature access.

Installation

Install the qiskit-braket-provider package:

pip install qiskit-braket-provider

Quick Circuit Conversion

The simplest approach is to use the to_braket conversion function to convert Qiskit circuits to Braket format:

Qiskit to Braket Circuit Conversion
import strangeworks as sw
from qiskit import QuantumCircuit
from qiskit_braket_provider import to_braket
from strangeworks_braket import StrangeworksDevice

# Authenticate with Strangeworks
sw.authenticate(api_key)

# Create a Qiskit quantum circuit
qc = QuantumCircuit(2)
qc.h(0) # Apply Hadamard gate to qubit 0
qc.cx(0, 1) # Apply CNOT gate with control=0, target=1
qc.measure_all() # Add measurements

print("Original Qiskit Circuit:")
print(qc.draw())

# Convert Qiskit circuit to Braket circuit
braket_qc = to_braket(qc)

print("\nConverted Braket Circuit:")
print(braket_qc)

# Execute on Braket through Strangeworks
device = StrangeworksDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")
task = device.run(braket_qc, shots=1000)

# Add tags for organization
sw.add_tags(task.id, ["qiskit-conversion", "bell-state"])

print(f"⏳ Task submitted with ID: {task.id}")

# Get results
result = task.result()
print(f"📊 Measurement counts: {result.measurement_counts}")

More Complex Example

Here's an example with a more complex Qiskit circuit:

Complex Qiskit Circuit Conversion
import strangeworks as sw
from qiskit import QuantumCircuit
from qiskit.circuit.library import QFT
from qiskit_braket_provider import to_braket
from strangeworks_braket import StrangeworksDevice
import numpy as np

sw.authenticate(api_key)

# Create a more complex Qiskit circuit with parameterized gates
qc = QuantumCircuit(3)

# Add some rotations and entanglement
qc.ry(np.pi/4, 0)
qc.ry(np.pi/3, 1)
qc.cx(0, 1)
qc.cx(1, 2)
qc.rz(np.pi/6, 2)
qc.barrier()

# Add QFT to the circuit
qft = QFT(num_qubits=3)
qc.compose(qft, inplace=True)
qc.measure_all()

print("Complex Qiskit Circuit:")
print(qc.draw())

try:
# Convert to Braket
braket_qc = to_braket(qc)
print("\n✅ Successfully converted to Braket!")

# Execute on Braket
device = StrangeworksDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")
task = device.run(braket_qc, shots=1000)

sw.add_tags(task.id, ["qiskit-conversion", "qft-circuit"])

result = task.result()
print(f"📊 Results: {result.measurement_counts}")

except Exception as e:
print(f"❌ Conversion failed: {e}")
print("💡 Consider rewriting this circuit in native Braket syntax")

Best Practices for Qiskit-Braket Conversion

  1. Test Conversions: Always test converted circuits on simulators first before running on hardware
  2. Simple Circuits: Start with simple circuits and gradually increase complexity
  3. Avoid Custom Gates: Standard Qiskit gates work best; avoid custom or composite gates
  4. Check Gate Support: Verify that all gates in your Qiskit circuit are supported in Braket
  5. Consider Rewriting: For production applications, rewrite complex circuits in native Braket syntax

Supported Gates

The conversion typically supports standard gates like:

  • Single-qubit gates: H, X, Y, Z, RX, RY, RZ, S, T
  • Two-qubit gates: CNOT, CZ, SWAP
  • Multi-qubit gates: Toffoli (CCX)
  • Measurement operations

When Conversion Might Fail

  • Custom or user-defined gates
  • Advanced Qiskit circuit features (conditional operations, etc.)
  • Some parameterized composite gates
  • Circuits with complex classical logic

Alternative: Direct Braket Implementation

For better control and guaranteed compatibility, consider implementing circuits directly in Braket:

Direct Braket Implementation
import strangeworks as sw
from braket.circuits import Circuit
from strangeworks_braket import StrangeworksDevice

sw.authenticate(api_key)

# Equivalent Bell state circuit in native Braket syntax
braket_circuit = Circuit().h(0).cnot(0, 1)

device = StrangeworksDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")
task = device.run(braket_circuit, shots=1000)

sw.add_tags(task.id, ["native-braket", "bell-state"])

result = task.result()
print(f"📊 Results: {result.measurement_counts}")

🥳 Success! You may view your job in the portal.

😅 Something went wrong? Contact us at support@strangeworks.com for assistance.