Running Circuits on Braket Simulators
Overview
# --- Setup cell added by QCR (not part of the original tutorial) ---
# Source: amazon-braket/amazon-braket-examples @ 0c0818f315479aab9deebed7e7ed7533ac581923, Apache License 2.0.
# Installs the example's dependencies. If a later cell still reports a missing
# package, restart the runtime/kernel and run again from the top.
%pip install -q amazon-braket-sdk==1.117.3 matplotlib
Preparing a GHZ state and running the circuit on simulators
# Use Braket SDK Cost Tracking to estimate the cost to run this example
from braket.tracking import Tracker
t = Tracker().start()This hello-world tutorial prepares a paradigmatic example for a multi-qubit entangled state, the so-called GHZ state (named after the three physicists Greenberger, Horne and Zeilinger). The GHZ state is extremely non-classical, and therefore very sensitive to decoherence. Therefore, it is often used as a performance benchmark for today's hardware. Moreover, in many quantum information protocols it is used as a resource for quantum error correction, quantum communication and quantum metrology.
Amazon Braket offers several classical simulators including a local simulator and three on-demand simulators: a full state-vector simulator SV1, a density matrix simulator DM1, and a tensor-network simulator TN1. You can seamlessly swap between different devices without any modifications to the circuit definition, as shown below, by just re-defining the device object. For additional information about simulators, see the Amazon Braket Dev Guide.
# general imports
import matplotlib.pyplot as plt
# magic word for producing visualizations in notebook
%matplotlib inline
import numpy as np
from braket.aws import AwsDevice
# AWS imports: Import Braket SDK modules
from braket.circuits import Circuit
from braket.circuits.observables import Z
from braket.devices import Devices, LocalSimulatorProblem: Prepare a GHZ State
Goal: Prepare an
The GHZ state is a quantum superposition of all subsystems being in state 0 with all of them being in state 1 (as often discussed in the famous Gedanken experiment of a cat being dead and alive at the same time). The GHZ state is a maximally entangled quantum state.
To prepare this state, build and run the following circuit using a single-qubit Hadamard gate (denoted as H) acting on the first qubit followed by a series of two-qubit CNOT gates:
Setup Circuit
# function to build a GHZ state
def ghz_circuit(n_qubits):
"""Function to return a GHZ circuit ansatz
input: number of qubits
"""
# instantiate circuit object
circuit = Circuit()
# add Hadamard gate on first qubit
circuit.h(0)
# apply series of CNOT gates
for ii in range(n_qubits - 1):
circuit.cnot(control=ii, target=ii + 1)
return circuit# define circuit
n_qubits = 10
ghz = ghz_circuit(n_qubits)# print circuit
print(ghz)T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
┌───┐
q0 : ─┤ H ├───●───────────────────────────────────────────────────
└───┘ │
┌─┴─┐
q1 : ───────┤ X ├───●─────────────────────────────────────────────
└───┘ │
┌─┴─┐
q2 : ─────────────┤ X ├───●───────────────────────────────────────
└───┘ │
┌─┴─┐
q3 : ───────────────────┤ X ├───●─────────────────────────────────
└───┘ │
┌─┴─┐
q4 : ─────────────────────────┤ X ├───●───────────────────────────
└───┘ │
┌─┴─┐
q5 : ───────────────────────────────┤ X ├───●─────────────────────
└───┘ │
┌─┴─┐
q6 : ─────────────────────────────────────┤ X ├───●───────────────
└───┘ │
┌─┴─┐
q7 : ───────────────────────────────────────────┤ X ├───●─────────
└───┘ │
┌─┴─┐
q8 : ─────────────────────────────────────────────────┤ X ├───●───
└───┘ │
┌─┴─┐
q9 : ───────────────────────────────────────────────────────┤ X ├─
└───┘
T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
Local Simulator
First, the circuit is run locally using the local simulator.
# set up device: Local Simulator
device = LocalSimulator()# run circuit
result = device.run(ghz, shots=1000).result()
# get measurement shots
counts = result.measurement_counts
# print counts
print(counts)Counter({'0000000000': 502, '1111111111': 498})
# plot using Counter
plt.bar(counts.keys(), counts.values())
plt.xlabel("bitstrings")
plt.ylabel("counts")Text(0, 0.5, 'counts')
As opposed to quantum hardware where only measurement shots can be obtained, with a classical simulator you can access the full state vector, amplitudes and expectation values of certain observables by assigning the corresponding result types. To do so, append the result types to the circuit before submitting it to run. This can be very useful for debugging.
The example code below outputs the state vector, the expectation value of
To reiterate, the following output is expected:
for which
# define circuit
n_qubits = 3
ghz = ghz_circuit(n_qubits)
# add the state_vector ResultType
ghz.state_vector()
# add the Z \otimes Z \otimes Z expectation value
ghz.expectation(Z(0) @ Z(1) @ Z(2))
# add the amplitude for |111>
ghz.amplitude(state=["111"])
# print circuit including requested result types
print(ghz)T : │ 0 │ 1 │ 2 │ Result Types │
┌───┐ ┌────────────────────┐
q0 : ─┤ H ├───●─────────┤ Expectation(Z@Z@Z) ├─
└───┘ │ └─────────┬──────────┘
┌─┴─┐ ┌─────────┴──────────┐
q1 : ───────┤ X ├───●───┤ Expectation(Z@Z@Z) ├─
└───┘ │ └─────────┬──────────┘
┌─┴─┐ ┌─────────┴──────────┐
q2 : ─────────────┤ X ├─┤ Expectation(Z@Z@Z) ├─
└───┘ └────────────────────┘
T : │ 0 │ 1 │ 2 │ Result Types │
Additional result types: StateVector, Amplitude(111)
# run the circuit and output the results
task = device.run(ghz, shots=0)
result = task.result()
# print results
print("Final EXACT state vector:\n", result.values[0])
print("Expectation value <ZZZ>:", np.round(result.values[1], 5))
print("Amplitude <111|Final state>:", result.values[2])Final EXACT state vector:
[0.70710678+0.j 0. +0.j 0. +0.j 0. +0.j
0. +0.j 0. +0.j 0. +0.j 0.70710678+0.j]
Expectation value <ZZZ>: 0.0
Amplitude <111|Final state>: {'111': (0.7071067811865475+0j)}
Clearly the expected results with perfect correlations between the three qubits making up the GHZ state are obtained.
Note that you can only request state vector and amplitude when shots = 0 for a classical simulator. When shots = 0 for a simulator, you get the exact values of probability, expectation values, and variance, as derived from the full wave function. When shots > 0, you cannot access the full state vector, but you can get approximate expectation values as taken from measurement samples. Note that Amazon Braket also supports probability, sample, expectation, and variance as result types for QPU devices.
The On-Demand Simulators
Apart from the local simulator, you can also run your circuit on an on-demand simulator. This approach adds some latency overhead, but is beneficial for larger circuits by leveraging the optimized cloud hardware infrastructure. Moreover, all your results will be stored reliably in S3.
Amazon Braket provides three on-demand simulators:
-
SV1
State vector simulator supports simulations of circuits with up to 34 qubits, calculates and keeps track of the full state vector evolution.
-
TN1
Tensor-network simulator represents each gate in a circuit as a tensor. TN1 can simulate a larger number of qubits for circuits with local gates or other special structure as compared with SV1 and DM1, but typically is slower for circuits with long-range or all-to-all gate structure.
-
DM1
Density matrix simulator stores the full density matrix of the system and sequentially applies gates and noise operations of the circuit.
To instantiate an on-demand simulator, pass the ARN of the device to AwsDevice:
# set up the on-demand simulator SV1
device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")You can also get the device's ARN from its corresponding Device enum value:
device = AwsDevice(Devices.Amazon.SV1)# define a 15-qubit GHZ circuit
n_qubits = 15
ghz = ghz_circuit(n_qubits)
# run GHZ circuit on SV1
result = device.run(ghz, shots=1000).result()
counts = result.measurement_counts
print(counts)
# plot using Counter
plt.bar(counts.keys(), counts.values())
plt.xlabel("bitstrings")
plt.ylabel("counts")
# print counts of all-zero-string
print("Counts for all-zero bitstring:", counts["0" * n_qubits])
# print counts of all-one-string
print("Counts for all-one bitstring:", counts["1" * n_qubits])Counter({'000000000000000': 520, '111111111111111': 480})
Counts for all-zero bitstring: 520
Counts for all-one bitstring: 480
The following example demonstrates that TN1 can easily simulate GHZ circuits with up to 50 qubits due to the sparse, nearest neighbor gate structure.
# set up the on-demand simulator TN1
device = AwsDevice(Devices.Amazon.TN1)# define a larger GHZ circuit
n_qubits = 50
ghz = ghz_circuit(n_qubits)
# run the same circuit on TN1
result = device.run(ghz, shots=1000).result()
counts = result.measurement_counts
print(counts)
# print counts of all-zero-string
print("Counts for all-zero bitstring:", counts["0" * n_qubits])
# print counts of all-one-string
print("Counts for all-one bitstring:", counts["1" * n_qubits])Counter({'00000000000000000000000000000000000000000000000000': 527, '11111111111111111111111111111111111111111111111111': 473})
Counts for all-zero bitstring: 527
Counts for all-one bitstring: 473
NOTE: Use unique quantum task ID to look up quantum task details in AWS console.
# print unique TASK ID (task = execution of individual circuit)
task_id = result.task_metadata.id
# recover other metadata information such as number of qubits
n = result.task_metadata.deviceParameters.paradigmParameters.qubitCount
# print('Task ID:', task_id)
print("Number of qubits:", n)Number of qubits: 50
print("Quantum Task Summary")
print(t.quantum_tasks_statistics())
print(
"Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2).",
)
print(
f"Estimated cost to run this example: {t.qpu_tasks_cost() + t.simulator_tasks_cost():.2f} USD",
)Quantum Task Summary
{<_Amazon.SV1: 'arn:aws:braket:::device/quantum-simulator/amazon/sv1'>: {'shots': 1000, 'tasks': {'COMPLETED': 1}, 'execution_duration': datetime.timedelta(microseconds=6000), 'billed_execution_duration': datetime.timedelta(seconds=3)}, <_Amazon.TN1: 'arn:aws:braket:::device/quantum-simulator/amazon/tn1'>: {'shots': 1000, 'tasks': {'COMPLETED': 1}, 'execution_duration': datetime.timedelta(seconds=4, microseconds=666000), 'billed_execution_duration': datetime.timedelta(seconds=4, microseconds=666000)}}
Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2).
Estimated cost to run this example: 0.03 USD
This entry was created automatically from publicly available records. QCR links to public sources and only stores repository content where the license permits redistribution.
Versions
Cite all versions? Use the base QCR ID to always reference the latest version of this entry.
Join the Discussion
Comments (0)
No comments yet. Be the first to share your thoughts!