diff --git a/README.md b/README.md
index 4ce110c89..47947c963 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
# Braket Tutorials GitHub
+
Welcome to the primary repository for Amazon Braket tutorials. We provide tutorials on quantum computing, using Amazon Braket. We provide examples for quantum circuits and Analog Hamiltonian Simulation. We cover canonical routines, such as the Quantum Fourier Transform (QFT), as well as hybrid quantum algorithms, such as the Variational Quantum Eigensolver (VQE).
The examples in this repository are structured as follows:
@@ -14,285 +15,299 @@ The examples in this repository are structured as follows:
- [Qiskit with Braket](#qiskit)
---
+
## I'm new to quantum
- * [**Getting started**](examples/getting_started/0_Getting_started/0_Getting_started.ipynb)
+- [**Getting started**](examples/getting_started/0_Getting_started/0_Getting_started.ipynb)
- A hello-world tutorial that shows you how to build a simple circuit and run it on a local simulator.
+ A hello-world tutorial that shows you how to build a simple circuit and run it on a local simulator.
- * [**Running quantum circuits on simulators**](examples/getting_started/1_Running_quantum_circuits_on_simulators/1_Running_quantum_circuits_on_simulators.ipynb)
+- [**Running quantum circuits on simulators**](examples/getting_started/1_Running_quantum_circuits_on_simulators/1_Running_quantum_circuits_on_simulators.ipynb)
- This 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. For this reason, 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.
+ This 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. For this reason, 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.
- * [**Running quantum circuits on QPU devices**](examples/getting_started/2_Running_quantum_circuits_on_QPU_devices/2_Running_quantum_circuits_on_QPU_devices.ipynb)
+- [**Running quantum circuits on QPU devices**](examples/getting_started/2_Running_quantum_circuits_on_QPU_devices/2_Running_quantum_circuits_on_QPU_devices.ipynb)
- This tutorial prepares a maximally-entangled Bell state between two qubits, for classical simulators and for QPUs. For classical devices, we can run the circuit on a local simulator or a cloud-based on-demand simulator. For the quantum devices, we run the circuit on the superconducting machine from Rigetti, and on the ion-trap machine provided by IonQ. As shown, one can swap between different devices seamlessly, without any modifications to the circuit definition, by re-defining the device object. We also show how to recover results using the unique Amazon resource identifier (ARN) associated with every quantum task. This tool is useful if you must deal with potential delays, which can occur if your quantum task sits in the queue awaiting execution.
+ This tutorial prepares a maximally-entangled Bell state between two qubits, for classical simulators and for QPUs. For classical devices, we can run the circuit on a local simulator or a cloud-based on-demand simulator. For the quantum devices, we run the circuit on the superconducting machine from Rigetti, and on the ion-trap machine provided by IonQ. As shown, one can swap between different devices seamlessly, without any modifications to the circuit definition, by re-defining the device object. We also show how to recover results using the unique Amazon resource identifier (ARN) associated with every quantum task. This tool is useful if you must deal with potential delays, which can occur if your quantum task sits in the queue awaiting execution.
- * [**Deep Dive into the anatomy of quantum circuits**](examples/getting_started/3_Deep_dive_into_the_anatomy_of_quantum_circuits/3_Deep_dive_into_the_anatomy_of_quantum_circuits.ipynb)
+- [**Deep Dive into the anatomy of quantum circuits**](examples/getting_started/3_Deep_dive_into_the_anatomy_of_quantum_circuits/3_Deep_dive_into_the_anatomy_of_quantum_circuits.ipynb)
- This tutorial discusses in detail the anatomy of quantum circuits in the Amazon Braket SDK. Specifically, you'll learn how to build (parameterized) circuits and display them graphically, and how to append circuits to each other. We discuss the associated circuit depth and circuit size. Finally we show how to execute the circuit on a device of our choice (defining a quantum task). We then learn how to track, log, recover, or cancel such a _quantum task_ efficiently.
+ This tutorial discusses in detail the anatomy of quantum circuits in the Amazon Braket SDK. Specifically, you'll learn how to build (parameterized) circuits and display them graphically, and how to append circuits to each other. We discuss the associated circuit depth and circuit size. Finally we show how to execute the circuit on a device of our choice (defining a quantum task). We then learn how to track, log, recover, or cancel such a _quantum task_ efficiently.
- * [**Superdense coding**](examples/getting_started/4_Superdense_coding/4_Superdense_coding.ipynb)
+- [**Superdense coding**](examples/getting_started/4_Superdense_coding/4_Superdense_coding.ipynb)
- This tutorial constructs an implementation of the _superdense coding_ protocol, by means of the Amazon Braket SDK. Superdense coding is a method of transmitting two classical bits by sending only one qubit. Starting with a pair of entanged qubits, the sender (_aka_ Alice) applies a certain quantum gate to their qubit and sends the result to the receiver (_aka_ Bob), who is then able to decode the full two-bit message.
+ This tutorial constructs an implementation of the _superdense coding_ protocol, by means of the Amazon Braket SDK. Superdense coding is a method of transmitting two classical bits by sending only one qubit. Starting with a pair of entanged qubits, the sender (_aka_ Alice) applies a certain quantum gate to their qubit and sends the result to the receiver (_aka_ Bob), who is then able to decode the full two-bit message.
----
-## Advanced circuits and algorithms
+---
- * [**Grover**](examples/advanced_circuits_algorithms/Grover/Grover.ipynb)
+## Advanced circuits and algorithms
- This tutorial provides a step-by-step walkthrough explaining Grover's quantum algorithm. We show how to build the corresponding quantum circuit with simple modular building blocks, by means of the Amazon Braket SDK. Specifically, we demonstrate how to build custom gates that are not part of the basic gate set provided by the SDK. A custom gate can used as a core quantum gate by registering it as a subroutine.
+- [**Grover**](examples/advanced_circuits_algorithms/Grover/Grover.ipynb)
- * [**Quantum Amplitude Amplification**](examples/advanced_circuits_algorithms/Quantum_Amplitude_Amplification/Quantum_Amplitude_Amplification.ipynb)
+ This tutorial provides a step-by-step walkthrough explaining Grover's quantum algorithm. We show how to build the corresponding quantum circuit with simple modular building blocks, by means of the Amazon Braket SDK. Specifically, we demonstrate how to build custom gates that are not part of the basic gate set provided by the SDK. A custom gate can used as a core quantum gate by registering it as a subroutine.
- This tutorial provides a detailed discussion and implementation of the Quantum Amplitude Amplification (QAA) algorithm, using the Amazon Braket SDK. QAA is a routine in quantum computing which generalizes the idea behind Grover's famous search algorithm, with applications across many quantum algorithms. In short, QAA uses an iterative approach to systematically increase the probability of finding one or multiple target states in a given search space. In a quantum computer, QAA can be used to obtain a quadratic speedup over several classical algorithms.
+- [**Quantum Amplitude Amplification**](examples/advanced_circuits_algorithms/Quantum_Amplitude_Amplification/Quantum_Amplitude_Amplification.ipynb)
- * [**Quantum Fourier Transform**](examples/advanced_circuits_algorithms/Quantum_Fourier_Transform/Quantum_Fourier_Transform.ipynb)
+ This tutorial provides a detailed discussion and implementation of the Quantum Amplitude Amplification (QAA) algorithm, using the Amazon Braket SDK. QAA is a routine in quantum computing which generalizes the idea behind Grover's famous search algorithm, with applications across many quantum algorithms. In short, QAA uses an iterative approach to systematically increase the probability of finding one or multiple target states in a given search space. In a quantum computer, QAA can be used to obtain a quadratic speedup over several classical algorithms.
- This tutorial provides a detailed implementation of the Quantum Fourier Transform (QFT) and the inverse QFT, using the Amazon Braket SDK. We provide two different implementations: with and without recursion. The QFT is an important subroutine to many quantum algorithms, most famously Shor's algorithm for factoring, and the quantum phase estimation (QPE) algorithm for estimating the eigenvalues of a unitary operator. The QFT can be performed efficiently on a quantum computer, using only O(n2) single-qubit Hadamard gates and two-qubit controlled phase shift gates, where 𝑛 is the number of qubits. We first review the basics of the quantum Fourier transform, and its relationship to the discrete (classical) Fourier transform. We then implement the QFT in code two ways: recursively and non-recursively. This notebook also showcases the Amazon Braket `circuit.subroutine` functionality, which allows one to define custom methods and add them to the Circuit class.
+- [**Quantum Fourier Transform**](examples/advanced_circuits_algorithms/Quantum_Fourier_Transform/Quantum_Fourier_Transform.ipynb)
- * [**Quantum Phase Estimation**](examples/advanced_circuits_algorithms/Quantum_Phase_Estimation/Quantum_Phase_Estimation.ipynb)
+ This tutorial provides a detailed implementation of the Quantum Fourier Transform (QFT) and the inverse QFT, using the Amazon Braket SDK. We provide two different implementations: with and without recursion. The QFT is an important subroutine to many quantum algorithms, most famously Shor's algorithm for factoring, and the quantum phase estimation (QPE) algorithm for estimating the eigenvalues of a unitary operator. The QFT can be performed efficiently on a quantum computer, using only O(n2) single-qubit Hadamard gates and two-qubit controlled phase shift gates, where 𝑛 is the number of qubits. We first review the basics of the quantum Fourier transform, and its relationship to the discrete (classical) Fourier transform. We then implement the QFT in code two ways: recursively and non-recursively. This notebook also showcases the Amazon Braket `circuit.subroutine` functionality, which allows one to define custom methods and add them to the Circuit class.
- This tutorial provides a detailed implementation of the Quantum Phase Estimation (QPE) algorithm, through the Amazon Braket SDK. The QPE algorithm is designed to estimate the eigenvalues of a unitary operator 𝑈; it is a very important subroutine to many quantum algorithms, most famously Shor's algorithm for factoring, and the HHL algorithm (named after the physicists Harrow, Hassidim and Lloyd) for solving linear systems of equations on a quantum computer. Moreover, eigenvalue problems can be found across many disciplines and application areas, including (for example) principal component analysis (PCA) as used in machine learning, or in the solution of differential equations as relevant across mathematics, physics, engineering and chemistry. We first review the basics of the QPE algorithm. We then implement the QPE algorithm in code using the Amazon Braket SDK, and we illustrate the application of the algorithm with simple examples. This notebook also showcases the Amazon Braket `circuit.subroutine` functionality, which allows you to use custom-built gates as if they were any other built-in gates. This tutorial is set up to run on the local simulator or the on-demand simulator. Changing between these devices requires changing only one line of code, as demonstrated below in cell.
+- [**Quantum Phase Estimation**](examples/advanced_circuits_algorithms/Quantum_Phase_Estimation/Quantum_Phase_Estimation.ipynb)
- * [**Randomness Generation**](examples/advanced_circuits_algorithms/Randomness/Randomness_Generation.ipynb)
+ This tutorial provides a detailed implementation of the Quantum Phase Estimation (QPE) algorithm, through the Amazon Braket SDK. The QPE algorithm is designed to estimate the eigenvalues of a unitary operator 𝑈; it is a very important subroutine to many quantum algorithms, most famously Shor's algorithm for factoring, and the HHL algorithm (named after the physicists Harrow, Hassidim and Lloyd) for solving linear systems of equations on a quantum computer. Moreover, eigenvalue problems can be found across many disciplines and application areas, including (for example) principal component analysis (PCA) as used in machine learning, or in the solution of differential equations as relevant across mathematics, physics, engineering and chemistry. We first review the basics of the QPE algorithm. We then implement the QPE algorithm in code using the Amazon Braket SDK, and we illustrate the application of the algorithm with simple examples. This notebook also showcases the Amazon Braket `circuit.subroutine` functionality, which allows you to use custom-built gates as if they were any other built-in gates. This tutorial is set up to run on the local simulator or the on-demand simulator. Changing between these devices requires changing only one line of code, as demonstrated below in cell.
- This tutorial provides a detailed implementation of a Quantum Random Number Generator (QRNG). It shows how to use two separate quantum processor units (QPUs) from different suppliers in Amazon Braket to supply two streams of weakly random bits. We then show how to generate physically secure randomness from these two weak sources by means of classical post-processing based on randomness extractors.
+- [**Randomness Generation**](examples/advanced_circuits_algorithms/Randomness/Randomness_Generation.ipynb)
- * [**Simon's Algorithm**](examples/advanced_circuits_algorithms/Simons_Algorithm/Simons_Algorithm.ipynb)
+ This tutorial provides a detailed implementation of a Quantum Random Number Generator (QRNG). It shows how to use two separate quantum processor units (QPUs) from different suppliers in Amazon Braket to supply two streams of weakly random bits. We then show how to generate physically secure randomness from these two weak sources by means of classical post-processing based on randomness extractors.
- This tutorial provides a detailed implementation of Simon’s algorithm, which shows the first example of an exponential speedup over the best known classical algorithm by using a quantum computer to solve a particular problem. Originally published in 1994, Simon’s algorithm was a precursor to Shor’s well-known factoring algorithm, and it served as inspiration for many of the seminal works in quantum computation that followed.
+- [**Simon's Algorithm**](examples/advanced_circuits_algorithms/Simons_Algorithm/Simons_Algorithm.ipynb)
+ This tutorial provides a detailed implementation of Simon’s algorithm, which shows the first example of an exponential speedup over the best known classical algorithm by using a quantum computer to solve a particular problem. Originally published in 1994, Simon’s algorithm was a precursor to Shor’s well-known factoring algorithm, and it served as inspiration for many of the seminal works in quantum computation that followed.
---
-## Hybrid quantum algorithms
- * [**QAOA**](examples/hybrid_quantum_algorithms/QAOA/QAOA_braket.ipynb)
+## Hybrid quantum algorithms
+
+- [**QAOA**](examples/hybrid_quantum_algorithms/QAOA/QAOA_braket.ipynb)
- This tutorial shows how to (approximately) solve binary combinatorial optimization problems, using the Quantum Approximate Optimization Algorithm (QAOA). The QAOA algorithm belongs to the class of _hybrid quantum algorithms_ (leveraging classical and quantum computers), which are widely believed to be the working horse for the current NISQ (noisy intermediate-scale quantum) era. In this NISQ era, QAOA is also an emerging approach for benchmarking quantum devices. It is a prime candidate for demonstrating a practical quantum speed-up on near-term NISQ device. To validate our approach, we benchmark our results with exact results as obtained from classical QUBO solvers.
+ This tutorial shows how to (approximately) solve binary combinatorial optimization problems, using the Quantum Approximate Optimization Algorithm (QAOA). The QAOA algorithm belongs to the class of _hybrid quantum algorithms_ (leveraging classical and quantum computers), which are widely believed to be the working horse for the current NISQ (noisy intermediate-scale quantum) era. In this NISQ era, QAOA is also an emerging approach for benchmarking quantum devices. It is a prime candidate for demonstrating a practical quantum speed-up on near-term NISQ device. To validate our approach, we benchmark our results with exact results as obtained from classical QUBO solvers.
- * [**VQE Chemistry**](examples/hybrid_quantum_algorithms/VQE_Chemistry/VQE_chemistry_braket.ipynb)
+- [**VQE Chemistry**](examples/hybrid_quantum_algorithms/VQE_Chemistry/VQE_chemistry_braket.ipynb)
- This tutorial shows how to implement the Variational Quantum Eigensolver (VQE) algorithm in Amazon Braket SDK to compute the potential energy surface (PES) for the Hydrogen molecule.
+ This tutorial shows how to implement the Variational Quantum Eigensolver (VQE) algorithm in Amazon Braket SDK to compute the potential energy surface (PES) for the Hydrogen molecule.
- * [**VQE Transverse Ising**](examples/hybrid_quantum_algorithms/VQE_Transverse_Ising/VQE_Transverse_Ising_Model.ipynb)
+- [**VQE Transverse Ising**](examples/hybrid_quantum_algorithms/VQE_Transverse_Ising/VQE_Transverse_Ising_Model.ipynb)
- This tutorial shows how to solve for the ground state of the Transverse Ising Model, which is arguably one of the most prominent, canonical quantum spin systems, using the variational quantum eigenvalue solver (VQE). The VQE algorithm belongs to the class of _hybrid quantum algorithms_ (leveraging classical andquantum computers), which are widely believed to be the working horse for the current NISQ (noisy intermediate-scale quantum) era. To validate our approach, we benchmark our results with exact results as obtained from a Jordan-Wigner transformation.
+ This tutorial shows how to solve for the ground state of the Transverse Ising Model, which is arguably one of the most prominent, canonical quantum spin systems, using the variational quantum eigenvalue solver (VQE). The VQE algorithm belongs to the class of _hybrid quantum algorithms_ (leveraging classical andquantum computers), which are widely believed to be the working horse for the current NISQ (noisy intermediate-scale quantum) era. To validate our approach, we benchmark our results with exact results as obtained from a Jordan-Wigner transformation.
---
+
## Quantum machine learning and optimization with PennyLane
- * [**Combining PennyLane with Amazon Braket**](examples/pennylane/0_Getting_started/0_Getting_started.ipynb)
- This tutorial shows you how to construct circuits and evaluate their gradients in PennyLane with execution performed using Amazon Braket.
+- [**Combining PennyLane with Amazon Braket**](examples/pennylane/0_Getting_started/0_Getting_started.ipynb)
- * [**Computing gradients in parallel with PennyLane-Braket**](examples/pennylane/1_Parallelized_optimization_of_quantum_circuits/1_Parallelized_optimization_of_quantum_circuits.ipynb)
+ This tutorial shows you how to construct circuits and evaluate their gradients in PennyLane with execution performed using Amazon Braket.
- In this tutorial, we explore how to speed up training of quantum circuits by using parallel execution on Amazon Braket. We begin by discussing why quantum circuit training involving gradients requires multiple device executions and motivate how the Braket SV1 simulator can be used to overcome this. The tutorial benchmarks SV1 against a local simulator, showing that SV1 outperforms the local simulator for both executions and gradient calculations. This illustrates how parallel capabilities can be combined between PennyLane and SV1.
+- [**Computing gradients in parallel with PennyLane-Braket**](examples/pennylane/1_Parallelized_optimization_of_quantum_circuits/1_Parallelized_optimization_of_quantum_circuits.ipynb)
- * [**Graph optimization with QAOA**](examples/pennylane/2_Graph_optimization_with_QAOA/2_Graph_optimization_with_QAOA.ipynb)
+ In this tutorial, we explore how to speed up training of quantum circuits by using parallel execution on Amazon Braket. We begin by discussing why quantum circuit training involving gradients requires multiple device executions and motivate how the Braket SV1 simulator can be used to overcome this. The tutorial benchmarks SV1 against a local simulator, showing that SV1 outperforms the local simulator for both executions and gradient calculations. This illustrates how parallel capabilities can be combined between PennyLane and SV1.
- In this tutorial we dig deeper into how quantum circuit training can be applied to a problem of practical relevance in graph optimization. We show how easy it is to train a QAOA circuit in PennyLane to solve the maximum clique problem on a simple example graph. The tutorial then extends to a more difficult 20-node graph and uses the parallel capabilities of the Amazon Braket SV1 simulator to speed up gradient calculations and hence train the quantum circuit faster, using around 1-2 minutes per iteration.
+- [**Graph optimization with QAOA**](examples/pennylane/2_Graph_optimization_with_QAOA/2_Graph_optimization_with_QAOA.ipynb)
- * [**Hydrogen geometry with VQE**](examples/pennylane/3_Hydrogen_Molecule_geometry_with_VQE/3_Hydrogen_Molecule_geometry_with_VQE.ipynb)
+ In this tutorial we dig deeper into how quantum circuit training can be applied to a problem of practical relevance in graph optimization. We show how easy it is to train a QAOA circuit in PennyLane to solve the maximum clique problem on a simple example graph. The tutorial then extends to a more difficult 20-node graph and uses the parallel capabilities of the Amazon Braket SV1 simulator to speed up gradient calculations and hence train the quantum circuit faster, using around 1-2 minutes per iteration.
- In this tutorial, we see how PennyLane and Amazon Braket can be combined to solve an important problem in quantum chemistry. The ground state energy of molecular hydrogen is calculated by optimizing a VQE circuit using the local Braket simulator. This tutorial highlights how qubit-wise commuting observables can be measured together in PennyLane and Braket, making optimization more efficient.
+- [**Hydrogen geometry with VQE**](examples/pennylane/3_Hydrogen_Molecule_geometry_with_VQE/3_Hydrogen_Molecule_geometry_with_VQE.ipynb)
- * [**Simulation of Noisy Circuits with PennyLane-Braket**](examples/pennylane/4_Simulation_of_noisy_quantum_circuits_on_Amazon_Braket_with_PennyLane/4_Simulation_of_noisy_quantum_circuits_on_Amazon_Braket_with_PennyLane.ipynb)
+ In this tutorial, we see how PennyLane and Amazon Braket can be combined to solve an important problem in quantum chemistry. The ground state energy of molecular hydrogen is calculated by optimizing a VQE circuit using the local Braket simulator. This tutorial highlights how qubit-wise commuting observables can be measured together in PennyLane and Braket, making optimization more efficient.
- In this tutorial, we explore the impact of noise on quantum hybrid algorithms and overview of noise simulation on Amazon Braket with PennyLane. The tutorial shows how to use PennyLane to simulate the noisy circuits, on either the local or Braket on-demand noise simulator, and covers the basic concepts of noise channels, using PennyLane to compute cost functions of noisy circuits and optimize them.
+- [**Simulation of Noisy Circuits with PennyLane-Braket**](examples/pennylane/4_Simulation_of_noisy_quantum_circuits_on_Amazon_Braket_with_PennyLane/4_Simulation_of_noisy_quantum_circuits_on_Amazon_Braket_with_PennyLane.ipynb)
- * [**Tracking Resource Usage**](examples/pennylane/5_Tracking_resource_usage/5_Tracking_resource_usage.ipynb)
+ In this tutorial, we explore the impact of noise on quantum hybrid algorithms and overview of noise simulation on Amazon Braket with PennyLane. The tutorial shows how to use PennyLane to simulate the noisy circuits, on either the local or Braket on-demand noise simulator, and covers the basic concepts of noise channels, using PennyLane to compute cost functions of noisy circuits and optimize them.
- In this tutorial, we see how to use the PennyLane device tracker feature with Amazon Braket. The PennyLane device resource tracker keeps a record of the usage of a device, such as numbers of circuit evaluations and shots. Amazon Braket extends this information with quantum task IDs and simulator duration to allow further tracking. The device tracker can be combined with additional logic to monitor and limit resource usage on devices.
+- [**Tracking Resource Usage**](examples/pennylane/5_Tracking_resource_usage/5_Tracking_resource_usage.ipynb)
- * [**Adjoint Gradient Computation**](examples/pennylane/6_Adjoint_gradient_computation/6_Adjoint_gradient_computation.ipynb)
+ In this tutorial, we see how to use the PennyLane device tracker feature with Amazon Braket. The PennyLane device resource tracker keeps a record of the usage of a device, such as numbers of circuit evaluations and shots. Amazon Braket extends this information with quantum task IDs and simulator duration to allow further tracking. The device tracker can be combined with additional logic to monitor and limit resource usage on devices.
- In this tutorial, we will show you how to compute gradients of free parameters in a quantum circuit using PennyLane and Amazon Braket. Adjoint differentiation is a technique used to compute gradients of parametrized quantum circuits. It can be used when shots=0 and is available on Amazon Braket’s on-demand state vector simulator, SV1. The adjoint differentiation method allows you to compute the gradient of a circuit with P parameters in only 1+1 circuit executions (one forward and one backward pass, similar to backpropagation), as opposed to the parameter-shift or finite-difference methods, both of which require 2P circuit executions for every gradient calculation. The adjoint method can lower the cost of running variational quantum workflows, especially for circuits with a large number of parameters.
+- [**Adjoint Gradient Computation**](examples/pennylane/6_Adjoint_gradient_computation/6_Adjoint_gradient_computation.ipynb)
+
+ In this tutorial, we will show you how to compute gradients of free parameters in a quantum circuit using PennyLane and Amazon Braket. Adjoint differentiation is a technique used to compute gradients of parametrized quantum circuits. It can be used when shots=0 and is available on Amazon Braket’s on-demand state vector simulator, SV1. The adjoint differentiation method allows you to compute the gradient of a circuit with P parameters in only 1+1 circuit executions (one forward and one backward pass, similar to backpropagation), as opposed to the parameter-shift or finite-difference methods, both of which require 2P circuit executions for every gradient calculation. The adjoint method can lower the cost of running variational quantum workflows, especially for circuits with a large number of parameters.
---
+
## Amazon Braket features
+
This folder contains examples that illustrate the usage of individual features of Amazon Braket
-* [**Getting notifications when a quantum task completes**](examples/braket_features/Getting_notifications_when_a_quantum_task_completes/Getting_notifications_when_a_quantum_task_completes.ipynb)
+- [**Getting notifications when a quantum task completes**](examples/braket_features/Getting_notifications_when_a_quantum_task_completes/Getting_notifications_when_a_quantum_task_completes.ipynb)
- This tutorial illustrates how Amazon Braket integrates with Amazon EventBridge for event-based processing. In the tutorial, you will learn how to configure Amazon Braket and Amazon Eventbridge to receive text notification about quantum task completions on your phone. Of course, EventBridge also allows you to build full, event-driven applications based on events emitted by Amazon Braket.
+ This tutorial illustrates how Amazon Braket integrates with Amazon EventBridge for event-based processing. In the tutorial, you will learn how to configure Amazon Braket and Amazon Eventbridge to receive text notification about quantum task completions on your phone. Of course, EventBridge also allows you to build full, event-driven applications based on events emitted by Amazon Braket.
-* [**Noise Models on Amazon Braket**](examples/braket_features/Noise_models/Noise_models_on_Amazon_Braket.ipynb)
+- [**Noise Models on Amazon Braket**](examples/braket_features/Noise_models/Noise_models_on_Amazon_Braket.ipynb)
- This tutorial shows how to create noise models containing different types of noise and instructions for how to apply the noise to a circuit. A noise model encapsulates the assumptions on quantum noise channels and how they act on a given circuit. Simulating this noisy circuit gives information about much the noise impacts the results of the quantum computation. By incrementally adjusting the noise model, the impact of noise can be understood on a variety of quantum algorithms.
+ This tutorial shows how to create noise models containing different types of noise and instructions for how to apply the noise to a circuit. A noise model encapsulates the assumptions on quantum noise channels and how they act on a given circuit. Simulating this noisy circuit gives information about much the noise impacts the results of the quantum computation. By incrementally adjusting the noise model, the impact of noise can be understood on a variety of quantum algorithms.
-* [**Noise Models on Rigetti's Aspen device**](examples/braket_features/Noise_models/Noise_models_on_Rigetti.ipynb)
+- [**Noise Models on Rigetti's device**](examples/braket_features/Noise_models/Noise_models_on_Rigetti.ipynb)
- This tutorial builds on the previous noise model tutorial to show how to construct a noise model from device calibration data for a Rigetti quantum processing unit (QPU). We compare the measurement outcomes of circuits run on a noisy simulator with the same circuits run on a QPU, to show that simulating circuits with noise models more closely mimics the QPU.
+ This tutorial builds on the previous noise model tutorial to show how to construct a noise model from device calibration data for a Rigetti quantum processing unit (QPU). We compare the measurement outcomes of circuits run on a noisy simulator with the same circuits run on a QPU, to show that simulating circuits with noise models more closely mimics the QPU.
-* [**Allocating Qubits on QPU Devices**](examples/braket_features/Allocating_Qubits_on_QPU_Devices.ipynb)
+- [**Allocating Qubits on QPU Devices**](examples/braket_features/Allocating_Qubits_on_QPU_Devices.ipynb)
- This tutorial explains how you can use the Amazon Braket SDK to allocate the qubit selection for your circuits manually, when running on QPUs.
+ This tutorial explains how you can use the Amazon Braket SDK to allocate the qubit selection for your circuits manually, when running on QPUs.
-* [**Error Mitigation**](examples/braket_features/Error_Mitigation_on_Amazon_Braket.ipynb)
+- [**Error Mitigation**](examples/braket_features/Error_Mitigation_on_Amazon_Braket.ipynb)
- This tutorial explains how to get started with using error mitigation on IonQ’s Aria QPU. You’ll learn how Aria’s two built-in error mitigation techniques work, how to switch between them, and the performance difference you can expect to see with and without these techniques for some problems.
-
-* [**Getting Devices and Checking Device Properties**](examples/braket_features/Getting_Devices_and_Checking_Device_Properties.ipynb)
+ This tutorial explains how to get started with using error mitigation on IonQ’s Aria QPU. You’ll learn how Aria’s two built-in error mitigation techniques work, how to switch between them, and the performance difference you can expect to see with and without these techniques for some problems.
- This example shows how to interact with the Amazon Braket GetDevice API to retrieve Amazon Braket devices (such as simulators and QPUs) programmatically, and how to gain access to their properties.
+- [**Getting Devices and Checking Device Properties**](examples/braket_features/Getting_Devices_and_Checking_Device_Properties.ipynb)
-* [**Getting Started with OpenQASM on Braket**](examples/braket_features/Getting_Started_with_OpenQASM_on_Braket.ipynb)
+ This example shows how to interact with the Amazon Braket GetDevice API to retrieve Amazon Braket devices (such as simulators and QPUs) programmatically, and how to gain access to their properties.
- This tutorial demonstrates how to submit OpenQASM quantum tasks to various devices on Braket and introduce some OpenQASM features available on Braket. OpenQASM is a popular, open source, human-readable and hardware-agnostic quantum circuit description language.
+- [**Getting Started with OpenQASM on Braket**](examples/braket_features/Getting_Started_with_OpenQASM_on_Braket.ipynb)
-* [**IonQ Native Gates**](examples/braket_features/IonQ_Native_Gates.ipynb)
+ This tutorial demonstrates how to submit OpenQASM quantum tasks to various devices on Braket and introduce some OpenQASM features available on Braket. OpenQASM is a popular, open source, human-readable and hardware-agnostic quantum circuit description language.
- This tutorial goes into details of IonQ’s native gates and their functionalities, enabling us to realize direct control over the quantum operations on the computer without compiler optimizations or error mitigation. It will discuss the native gates available on IonQ, their mathematical representations, and how they can be used for applications such as the quantum Fourier transform (QFT).
+- [**IonQ Native Gates**](examples/braket_features/IonQ_Native_Gates.ipynb)
-* [**Advanced OpenQASM programs using the Local Simulator**](examples/braket_features/Simulating_Advanced_OpenQASM_Programs_with_the_Local_Simulator.ipynb)
+ This tutorial goes into details of IonQ’s native gates and their functionalities, enabling us to realize direct control over the quantum operations on the computer without compiler optimizations or error mitigation. It will discuss the native gates available on IonQ, their mathematical representations, and how they can be used for applications such as the quantum Fourier transform (QFT).
- This notebook serves as a reference of OpenQASM features supported by Braket with the LocalSimulator.
+- [**Advanced OpenQASM programs using the Local Simulator**](examples/braket_features/Simulating_Advanced_OpenQASM_Programs_with_the_Local_Simulator.ipynb)
-* [**Using the tensor network simulator TN1**](examples/braket_features/Using_the_tensor_network_simulator_TN1.ipynb)
+ This notebook serves as a reference of OpenQASM features supported by Braket with the LocalSimulator.
- This notebook introduces the Amazon Braket on-demand tensor network simulator, TN1. You will learn about how TN1 works, how to use it, and which problems are best suited to run on TN1.
+- [**Using the tensor network simulator TN1**](examples/braket_features/Using_the_tensor_network_simulator_TN1.ipynb)
-* [**Simulating noise on Amazon Braket**](examples/braket_features/Simulating_Noise_On_Amazon_Braket.ipynb)
+ This notebook introduces the Amazon Braket on-demand tensor network simulator, TN1. You will learn about how TN1 works, how to use it, and which problems are best suited to run on TN1.
- This notebook provides a detailed overview of noise simulation on Amazon Braket. You will learn how to define noise channels, apply noise to new or existing circuits, and run those circuits on the Amazon Braket noise simulators.
+- [**Simulating noise on Amazon Braket**](examples/braket_features/Simulating_Noise_On_Amazon_Braket.ipynb)
-* [**TN1 and Hayden-Preskill circuits**](examples/braket_features/TN1_demo_local_vs_non-local_random_circuits.ipynb)
+ This notebook provides a detailed overview of noise simulation on Amazon Braket. You will learn how to define noise channels, apply noise to new or existing circuits, and run those circuits on the Amazon Braket noise simulators.
- This tutorial dives into showing the degree to which the tensor network simulator is capable of detecting a hidden local structure in a quantum circuit by working with Hayden-Preskill circuits, which are a class of unstructured, random quantum circuits.
+- [**TN1 and Hayden-Preskill circuits**](examples/braket_features/TN1_demo_local_vs_non-local_random_circuits.ipynb)
-* [**Adjoint Gradient Result Type**](examples/braket_features/Using_The_Adjoint_Gradient_Result_Type.ipynb)
+ This tutorial dives into showing the degree to which the tensor network simulator is capable of detecting a hidden local structure in a quantum circuit by working with Hayden-Preskill circuits, which are a class of unstructured, random quantum circuits.
- This tutorial introduces the AdjointGradient result type, discusses what a gradient is and how to compute one on a quantum circuit, explains how they can be used to accelerate your workflows, and shows an example of gradients in action on a hybrid quantum algorithm.
+- [**Adjoint Gradient Result Type**](examples/braket_features/Using_The_Adjoint_Gradient_Result_Type.ipynb)
-* [**Verbatim Compilation**](examples/braket_features/Verbatim_Compilation.ipynb)
+ This tutorial introduces the AdjointGradient result type, discusses what a gradient is and how to compute one on a quantum circuit, explains how they can be used to accelerate your workflows, and shows an example of gradients in action on a hybrid quantum algorithm.
- This tutorial explains how to use _verbatim compilation_ to run your circuits exactly as defined without any modification during the compilation process that's usually done behind-the-scenes when you run your circuits.
+- [**Verbatim Compilation**](examples/braket_features/Verbatim_Compilation.ipynb)
-* [**IQM Garnet Native Gates**](examples/braket_features/IQM_Garnet_Native_Gates.ipynb)
+ This tutorial explains how to use _verbatim compilation_ to run your circuits exactly as defined without any modification during the compilation process that's usually done behind-the-scenes when you run your circuits.
- This tutorial explores the functionality of the native gates of IQM Garnet.
+- [**IQM Garnet Native Gates**](examples/braket_features/IQM_Garnet_Native_Gates.ipynb)
-* [**Using the experimental local simulator**](examples/braket_features/Using_the_experimental_local_simulator.ipynb)
+ This tutorial explores the functionality of the native gates of IQM Garnet.
- This tutorial serves as an introduction to the experimental v2 local simulator for Amazon Braket. This tutorial explains how to use the v2 local simulator and the performance difference you can expect to see.
+- [**Using the experimental local simulator**](examples/braket_features/Using_the_experimental_local_simulator.ipynb)
+
+ This tutorial serves as an introduction to the experimental v2 local simulator for Amazon Braket. This tutorial explains how to use the v2 local simulator and the performance difference you can expect to see.
---
+
## Amazon Braket Hybrid Jobs
+
This folder contains examples that illustrate the use of Amazon Braket Hybrid Jobs (Braket Jobs for short).
-* [**Getting started with Amazon Braket Hybrid Jobs**](examples/hybrid_jobs/0_Creating_your_first_Hybrid_Job/0_Creating_your_first_Hybrid_Job.ipynb)
+- [**Getting started with Amazon Braket Hybrid Jobs**](examples/hybrid_jobs/0_Creating_your_first_Hybrid_Job/0_Creating_your_first_Hybrid_Job.ipynb)
- This notebook provides a demonstration of running a simple Braket Hybrid Job. You will learn how to create a Braket Hybrid Job using the Braket SDK or the Braket console, how to set the output S3 folder for a hybrid job, and how to retrieve results. You will also learn how to specify the Braket device to run your hybrid job on simulators or QPUs. Finally, you will learn how to use local mode to quickly debug your code.
+ This notebook provides a demonstration of running a simple Braket Hybrid Job. You will learn how to create a Braket Hybrid Job using the Braket SDK or the Braket console, how to set the output S3 folder for a hybrid job, and how to retrieve results. You will also learn how to specify the Braket device to run your hybrid job on simulators or QPUs. Finally, you will learn how to use local mode to quickly debug your code.
-* [**Quantum machine learning in Amazon Braket Hybrid Jobs**](examples/hybrid_jobs/1_Quantum_machine_learning_in_Amazon_Braket_Hybrid_Jobs/Quantum_machine_learning_in_Amazon_Braket_Hybrid_Jobs.ipynb)
+- [**Quantum machine learning in Amazon Braket Hybrid Jobs**](examples/hybrid_jobs/1_Quantum_machine_learning_in_Amazon_Braket_Hybrid_Jobs/Quantum_machine_learning_in_Amazon_Braket_Hybrid_Jobs.ipynb)
- This notebook shows a typical quantum machine learning workflow using Braket Hybrid Jobs. In the process, you will learn how to upload input data, how to set up hyperparameters for your hybrid job, and how to retrieve and plot metrics. Finally, you will see how to run multiple Braket Hybrid Jobs in parallel with different sets of hyperparameters.
+ This notebook shows a typical quantum machine learning workflow using Braket Hybrid Jobs. In the process, you will learn how to upload input data, how to set up hyperparameters for your hybrid job, and how to retrieve and plot metrics. Finally, you will see how to run multiple Braket Hybrid Jobs in parallel with different sets of hyperparameters.
-* [**QAOA with Amazon Braket Hybrid Jobs and PennyLane**](examples/hybrid_jobs/2_Using_PennyLane_with_Braket_Hybrid_Jobs/Using_PennyLane_with_Braket_Hybrid_Jobs.ipynb)
+- [**QAOA with Amazon Braket Hybrid Jobs and PennyLane**](examples/hybrid_jobs/2_Using_PennyLane_with_Braket_Hybrid_Jobs/Using_PennyLane_with_Braket_Hybrid_Jobs.ipynb)
- This notebook shows how to run the QAOA algorithm with PennyLane (similar to a [previous notebook](examples/pennylane/2_Graph_optimization_with_QAOA/2_Graph_optimization_with_QAOA.ipynb)), but this time using Braket Hybrid Jobs. In the process, you will learn how to select a container image that supports PennyLane, and how to use checkpoints to save and load training progress of a hybrid job.
+ This notebook shows how to run the QAOA algorithm with PennyLane (similar to a [previous notebook](examples/pennylane/2_Graph_optimization_with_QAOA/2_Graph_optimization_with_QAOA.ipynb)), but this time using Braket Hybrid Jobs. In the process, you will learn how to select a container image that supports PennyLane, and how to use checkpoints to save and load training progress of a hybrid job.
-* [**Bring your own containers to Braket Hybrid Jobs**](examples/hybrid_jobs/3_Bring_your_own_container/bring_your_own_container.ipynb)
+- [**Bring your own containers to Braket Hybrid Jobs**](examples/hybrid_jobs/3_Bring_your_own_container/bring_your_own_container.ipynb)
- This notebook demonstrates the use of the Bring-Your-Own-Container (BYOC) functionality of Braket Hybrid Jobs. While Amazon Braket has pre-configured environments which support most use cases of Braket Hybrid Jobs, BYOC enables you to define fully customizable environments using Docker containers. You will learn how to use BYOC, including preparing a Dockerfile, creating a private Amazon Elastic Container Registry (ECR), building the container, and submitting a Braket Hybrid Job using the custom container.
+ This notebook demonstrates the use of the Bring-Your-Own-Container (BYOC) functionality of Braket Hybrid Jobs. While Amazon Braket has pre-configured environments which support most use cases of Braket Hybrid Jobs, BYOC enables you to define fully customizable environments using Docker containers. You will learn how to use BYOC, including preparing a Dockerfile, creating a private Amazon Elastic Container Registry (ECR), building the container, and submitting a Braket Hybrid Job using the custom container.
-* [**Embedded simulators in Braket Hybrid Jobs**](examples/hybrid_jobs/4_Embedded_simulators_in_Braket_Hybrid_Jobs/Embedded_simulators_in_Braket_Hybrid_Jobs.ipynb)
+- [**Embedded simulators in Braket Hybrid Jobs**](examples/hybrid_jobs/4_Embedded_simulators_in_Braket_Hybrid_Jobs/Embedded_simulators_in_Braket_Hybrid_Jobs.ipynb)
- This notebook shows how to use embedded simulators in Braket Hybrid Jobs. An embedded simulator is a local simulator that runs completely within a hybrid job instance, i.e., the compute resource that is running your algorithm script. In contrast, on-demand simulators, such as SV1, DM1, or TN1, calculate the results of a quantum circuit on dedicated compute infrastructure on-demand by Amazon Braket. Hybrid workloads usually consist of iterations of quantum circuit executions and variational parameter optimizations. By using embedded simulators, we keep all computations in the same environment. This allows the optimization algorithm to access advanced features supported by the embedded simulator.
+ This notebook shows how to use embedded simulators in Braket Hybrid Jobs. An embedded simulator is a local simulator that runs completely within a hybrid job instance, i.e., the compute resource that is running your algorithm script. In contrast, on-demand simulators, such as SV1, DM1, or TN1, calculate the results of a quantum circuit on dedicated compute infrastructure on-demand by Amazon Braket. Hybrid workloads usually consist of iterations of quantum circuit executions and variational parameter optimizations. By using embedded simulators, we keep all computations in the same environment. This allows the optimization algorithm to access advanced features supported by the embedded simulator.
-* [**Parallelize training for Quantum Machine Learning**](examples/hybrid_jobs/5_Parallelize_training_for_QML/Parallelize_training_for_QML.ipynb)
+- [**Parallelize training for Quantum Machine Learning**](examples/hybrid_jobs/5_Parallelize_training_for_QML/Parallelize_training_for_QML.ipynb)
- This notebook introduces using data parallelism for Quantum Machine Learning (QML) workloads.
+ This notebook introduces using data parallelism for Quantum Machine Learning (QML) workloads.
-* [**QN-SPSA optimizer using an Embedded Simulator**](examples/hybrid_jobs/6_QNSPSA_optimizer_with_embedded_simulator/qnspsa_with_embedded_simulator.ipynb)
+- [**QN-SPSA optimizer using an Embedded Simulator**](examples/hybrid_jobs/6_QNSPSA_optimizer_with_embedded_simulator/qnspsa_with_embedded_simulator.ipynb)
- This notebook demonstrates how to implement and benchmark the QN-SPSA optimizer, a novel quantum optimization algorithm.
+ This notebook demonstrates how to implement and benchmark the QN-SPSA optimizer, a novel quantum optimization algorithm.
-* [**Running Jupyter notebooks as a Hybrid Job**](examples/hybrid_jobs/7_Running_notebooks_as_hybrid_jobs/Running_notebooks_as_hybrid_jobs.ipynb)
+- [**Running Jupyter notebooks as a Hybrid Job**](examples/hybrid_jobs/7_Running_notebooks_as_hybrid_jobs/Running_notebooks_as_hybrid_jobs.ipynb)
- This tutorial is a step-by-step guide for running a Jupyter notebook as a Hybrid Job.
+ This tutorial is a step-by-step guide for running a Jupyter notebook as a Hybrid Job.
-* [**Creating Hybrid Job Scripts**](examples/hybrid_jobs/8_Creating_Hybrid_Job_Scripts/Creating_your_first_Hybrid_Job.ipynb)
+- [**Creating Hybrid Job Scripts**](examples/hybrid_jobs/8_Creating_Hybrid_Job_Scripts/Creating_your_first_Hybrid_Job.ipynb)
- This notebook shows an alternate way to create a Hybrid Job without using a @hybrid_job decorator. The demonstrated method may be useful in some circumstances, such as using older versions of Python.
+ This notebook shows an alternate way to create a Hybrid Job without using a @hybrid_job decorator. The demonstrated method may be useful in some circumstances, such as using older versions of Python.
---
+
## Pulse Control
-* [**Bringup Experiments**](examples/pulse_control/1_Bringup_experiments.ipynb)
+- [**Bringup Experiments**](examples/pulse_control/1_Bringup_experiments.ipynb)
- This tutorial introduces common pulse sequences and calibrating pulses via Rabi spectroscopy.
+ This tutorial introduces common pulse sequences and calibrating pulses via Rabi spectroscopy.
-* [**Native Gate Calibrations**](examples/pulse_control/2_Native_gate_calibrations.ipynb)
+- [**Native Gate Calibrations**](examples/pulse_control/2_Native_gate_calibrations.ipynb)
- This tutorial shows how to retrieve the calibrations of native gates for Rigetti's Ankaa devices and submit a circuit with custom gate calibrations.
+ This tutorial shows how to retrieve the calibrations of native gates for Rigetti's Ankaa devices and submit a circuit with custom gate calibrations.
-* [**Bell pair with pulses (Rigetti)**](examples/pulse_control/3_Bell_pair_with_pulses_Rigetti.ipynb)
+- [**Bell pair with pulses (Rigetti)**](examples/pulse_control/3_Bell_pair_with_pulses_Rigetti.ipynb)
- This tutorial shows creating a Bell state with cross-resonance pulses on Rigetti's Ankaa device.
+ This tutorial shows creating a Bell state with cross-resonance pulses on Rigetti's Ankaa device.
-* [**Build single qubit gates**](examples/pulse_control/4_Build_single_qubit_gates.ipynb)
+- [**Build single qubit gates**](examples/pulse_control/4_Build_single_qubit_gates.ipynb)
- This tutorial describes a method to create any single-qubit gate with pulses.
+ This tutorial describes a method to create any single-qubit gate with pulses.
---
+
## Analog Hamiltonian Simulation
-* [**Intruction to Analog Hamiltonian Simulation**](examples/analog_hamiltonian_simulation/00_Introduction_of_Analog_Hamiltonian_Simulation_with_Rydberg_Atoms.ipynb)
+- [**Introduction to Analog Hamiltonian Simulation**](examples/analog_hamiltonian_simulation/00_Introduction_of_Analog_Hamiltonian_Simulation_with_Rydberg_Atoms.ipynb)
- This tutorial provides an introduction to Analog Hamiltonian Simulation (AHS), a quantum computing paradigm different from gate-based computing. AHS uses a well-controlled quantum system and tunes its parameters to mimic the dynamics of another quantum system, the one we aim to study.
+ This tutorial provides an introduction to Analog Hamiltonian Simulation (AHS), a quantum computing paradigm different from gate-based computing. AHS uses a well-controlled quantum system and tunes its parameters to mimic the dynamics of another quantum system, the one we aim to study.
-* [**Intruction to Aquila**](examples/analog_hamiltonian_simulation/01_Introduction_to_Aquila.ipynb)
+- [**Introduction to Aquila**](examples/analog_hamiltonian_simulation/01_Introduction_to_Aquila.ipynb)
- This tutorial illustrates how to run an AHS program on QuEra’s Aquila, a Rydberg based QPU, via Amazon Braket.
+ This tutorial illustrates how to run an AHS program on QuEra’s Aquila, a Rydberg based QPU, via Amazon Braket.
-* [**Ordered Phases in Rydberg Systems**](examples/analog_hamiltonian_simulation/02_Ordered_phases_in_Rydberg_systems.ipynb)
+- [**Ordered Phases in Rydberg Systems**](examples/analog_hamiltonian_simulation/02_Ordered_phases_in_Rydberg_systems.ipynb)
- This tutorial shows how to prepare ordered phases in Rydberg systems, focusing on the 1D phase and the 2D checkerboard phase. It uses an adiabatic time-evolution to prepare these many-body ground states.
+ This tutorial shows how to prepare ordered phases in Rydberg systems, focusing on the 1D phase and the 2D checkerboard phase. It uses an adiabatic time-evolution to prepare these many-body ground states.
-* [**Parallel Tasks on Aquila**](examples/analog_hamiltonian_simulation/03_Parallel_tasks_on_Aquila.ipynb)
+- [**Parallel Tasks on Aquila**](examples/analog_hamiltonian_simulation/03_Parallel_tasks_on_Aquila.ipynb)
- This tutorial builds on the previous notebook tutorial to use a checkerboard preparation that takes advantage of the full area.
+ This tutorial builds on the previous notebook tutorial to use a checkerboard preparation that takes advantage of the full area.
-* [**Maximum Independent Sets**](examples/analog_hamiltonian_simulation/04_Maximum_Independent_Sets_with_Analog_Hamiltonian_Simulation.ipynb)
+- [**Maximum Independent Sets**](examples/analog_hamiltonian_simulation/04_Maximum_Independent_Sets_with_Analog_Hamiltonian_Simulation.ipynb)
- This tutorial demonstrates how to set up a unit disk graph and solve for its maximum independent set using the Amazon Braket analog Hamiltonian simulation (AHS) local simulator.
+ This tutorial demonstrates how to set up a unit disk graph and solve for its maximum independent set using the Amazon Braket analog Hamiltonian simulation (AHS) local simulator.
-* [**Running on Local Simulator**](examples/analog_hamiltonian_simulation/05_Running_Analog_Hamiltonian_Simulation_with_local_simulator.ipynb)
+- [**Running on Local Simulator**](examples/analog_hamiltonian_simulation/05_Running_Analog_Hamiltonian_Simulation_with_local_simulator.ipynb)
- This tutorial shows how to test and debug an analog Hamiltonian simulation (AHS) program on the local simulator before submitting it to a QPU. It introduces several features of the local simulator that will be useful to streamline this testing process.
+ This tutorial shows how to test and debug an analog Hamiltonian simulation (AHS) program on the local simulator before submitting it to a QPU. It introduces several features of the local simulator that will be useful to streamline this testing process.
-* [**Simulation with PennyLane**](examples/analog_hamiltonian_simulation/06_Analog_Hamiltonian_simulation_with_PennyLane.ipynb)
+- [**Simulation with PennyLane**](examples/analog_hamiltonian_simulation/06_Analog_Hamiltonian_simulation_with_PennyLane.ipynb)
- This tutorial shows how to run analog Hamiltonian simulation (AHS) on Braket’s Rydberg atom devices leveraging quantum machine learning techniques from PennyLane.
+ This tutorial shows how to run analog Hamiltonian simulation (AHS) on Braket’s Rydberg atom devices leveraging quantum machine learning techniques from PennyLane.
-* [**Simulating lattice gauge theory with Rydberg atoms**](examples/analog_hamiltonian_simulation/07_Simulating_Lattice_Gauge_Theory_with_Rydberg_Atoms.ipynb)
+- [**Simulating lattice gauge theory with Rydberg atoms**](examples/analog_hamiltonian_simulation/07_Simulating_Lattice_Gauge_Theory_with_Rydberg_Atoms.ipynb)
- This tutorial shows how to prepare a specific initial state, using local detuning, to simulate the dynamics of a lattice gauge theory.
+ This tutorial shows how to prepare a specific initial state, using local detuning, to simulate the dynamics of a lattice gauge theory.
-* [**Maximum weight independent set**](examples/analog_hamiltonian_simulation/08_Maximum_Weight_Independent_Set.ipynb)
+- [**Maximum weight independent set**](examples/analog_hamiltonian_simulation/08_Maximum_Weight_Independent_Set.ipynb)
This tutorial generalizes the approach to solve the maximum weight independent set (MWIS) problem.
-* [**Noisy quantum dynamics**](examples/analog_hamiltonian_simulation/09_Noisy_quantum_dynamics_for_Rydberg_atom_arrays.ipynb)
+- [**Noisy quantum dynamics**](examples/analog_hamiltonian_simulation/09_Noisy_quantum_dynamics_for_Rydberg_atom_arrays.ipynb)
This tutorial shows how to run noise simulation on Braket’s Rydberg atom devices
-
+
---
-## Qiskit with Braket
-* [**Getting started with Qiskit on Amazon Braket**](examples/qiskit/0_Getting_Started.ipynb)
+## Qiskit with Braket
- This tutorial shows how you can run your Qiskit code on Amazon Braket computing services.
+- [**Getting started with Qiskit on Amazon Braket**](examples/qiskit/0_Getting_Started.ipynb)
+ This tutorial shows how you can run your Qiskit code on Amazon Braket computing services.
---
+
## Still can't find what you're looking for?
-Braket provides other libraries, tools, algorithms, experimental features, and more to help with your quantum computing journey. You can, for example, search all of our repositories for the [Bernstein Vazirani](https://github.com/search?q=org%3Aamazon-braket+Bernstein&type=code) algorithm or more [experimental features](https://github.com/search?q=org%3Aamazon-braket+experimental+features&type=code).
+
+Braket provides other libraries, tools, algorithms, experimental features, and more to help with your quantum computing journey. You can, for example, search all of our repositories for the [Bernstein Vazirani](https://github.com/search?q=org%3Aamazon-braket+Bernstein&type=code) algorithm or more [experimental features](https://github.com/search?q=org%3Aamazon-braket+experimental+features&type=code).
---
+
## Creating a conda environment
+
To install the dependencies required for running the notebook examples in this repository you can create a conda environment with below commands.
```bash
@@ -300,11 +315,13 @@ conda env create -n -f environment.yml
```
Activate the conda environment using:
+
```bash
conda activate
```
To remove the conda environment use:
+
```bash
conda deactivate
```
@@ -320,16 +337,17 @@ export AWS_PROFILE=YOUR_PROFILE_NAME
```
---
+
## Support
### Issues and Bug Reports
-If you encounter bugs or face issues while using the examples, please let us know by posting
+If you encounter bugs or face issues while using the examples, please let us know by posting
the issue on our [Github issue tracker](https://github.com/amazon-braket/amazon-braket-examples/issues/).
For other issues or general questions, please ask on the [Quantum Computing Stack Exchange](https://quantumcomputing.stackexchange.com/questions/ask) and add the tag [amazon-braket](https://quantumcomputing.stackexchange.com/questions/tagged/amazon-braket).
### Feedback and Feature Requests
If you have feedback or features that you would like to see on Amazon Braket, we would love to hear from you!
-[Github issues](https://github.com/amazon-braket/amazon-braket-examples/issues/) is our preferred mechanism for collecting feedback and feature requests, allowing other users
-to engage in the conversation, and +1 issues to help drive priority.
+[Github issues](https://github.com/amazon-braket/amazon-braket-examples/issues/) is our preferred mechanism for collecting feedback and feature requests, allowing other users
+to engage in the conversation, and +1 issues to help drive priority.
diff --git a/examples/braket_features/Allocating_Qubits_on_QPU_Devices.ipynb b/examples/braket_features/Allocating_Qubits_on_QPU_Devices.ipynb
index 69ffdee35..da493676b 100644
--- a/examples/braket_features/Allocating_Qubits_on_QPU_Devices.ipynb
+++ b/examples/braket_features/Allocating_Qubits_on_QPU_Devices.ipynb
@@ -1,541 +1,556 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Allocating Qubits on QPU Devices"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Use Braket SDK Cost Tracking to estimate the cost to run this example\n",
- "from braket.tracking import Tracker\n",
- "t = Tracker().start()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "This notebook demonstrates how you can specify explicitly which qubits to use when you run a quantum circuit on QPU devices from Rigetti.\n",
- "\n",
- "When you submit a circuit for execution on a QPU, Amazon Braket performs a series of compilation steps: it maps the _abstract qubits_ in your circuit to _physical qubits_ in the device; it synthesizes gates into the native gate set of the device; it optimizes the circuit to reduce the number of gates; and finally, it translates the gates into executable pulses.\n",
- "\n",
- "This section shows how the first step, called qubit allocation, works for the Rigetti Aspen-M-3 device."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "# general imports\n",
- "from braket.aws import AwsDevice\n",
- "from braket.circuits import Circuit\n",
- "from braket.devices import Devices\n",
- "import numpy as np\n",
- "import random"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "pycharm": {
- "name": "#%% md\n"
- }
- },
- "source": [
- "## Automatic qubit allocation\n",
- "\n",
- "Qubit allocation for Rigetti devices on Amazon Braket utilizes [the Quil Compilers](https://pyquil-docs.rigetti.com/en/latest/compiler.html#the-quil-compiler)'s _rewiring_ strategies. By default, when you submit a circuit on Amazon Braket to a Rigetti device, the circuit is rewired according to the [PARTIAL](https://pyquil-docs.rigetti.com/en/latest/compiler.html#partial) rewiring strategy. Specifically, the compiler starts with an empty mapping from logical to physical qubits. Taking into account the latest calibration data of the device, the compiler fills in the mapping with the goal, sequentially, to maximize the overall fidelity of the circuit.\n",
- "\n",
- "The example that follows shows how to create a GHZ state on qubits that are not physically connected. After the quantum task is completed, you can obtain a list of the actual gates executed on the device, by viewing the result metadata.\n",
- "\n",
- "First, instantiate the Rigetti Aspen-M-3 device and retrieve its connectivity graph, which shows the qubits that are directly connected on the chip."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {
- "pycharm": {
- "name": "#%%\n"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "the connectivity of Aspen-M-3 is: {'0': ['1', '7'], '1': ['0', '16', '2'], '10': ['11', '113', '17'], '100': ['101', '107'], '101': ['100', '102', '116'], '102': ['101', '103', '115'], '103': ['102', '104'], '104': ['103', '105', '7'], '105': ['104', '106'], '106': ['105', '107'], '107': ['100', '106'], '11': ['10', '12', '26'], '110': ['111', '117'], '111': ['110', '112', '126'], '112': ['111', '113'], '113': ['10', '112', '114'], '114': ['113', '115', '17'], '115': ['102', '114', '116'], '116': ['101', '115', '117'], '117': ['110', '116'], '12': ['11', '13', '25'], '120': ['121', '127'], '121': ['120', '122', '136'], '122': ['121', '123', '135'], '123': ['122', '124', '20'], '124': ['123', '125', '27'], '125': ['124', '126'], '126': ['111', '125', '127'], '127': ['120', '126'], '13': ['12', '14'], '130': ['131', '137'], '131': ['130', '132', '146'], '132': ['131', '133', '145'], '133': ['132', '134', '30'], '134': ['133', '135', '37'], '135': ['122', '134', '136'], '136': ['121', '135', '137'], '137': ['130', '136'], '14': ['13', '15'], '140': ['141', '147'], '141': ['140', '142'], '142': ['141', '143'], '143': ['142', '144', '40'], '144': ['143', '145', '47'], '145': ['132', '144', '146'], '146': ['131', '145', '147'], '147': ['140', '146'], '15': ['14', '16', '2'], '16': ['1', '15', '17'], '17': ['10', '16', '114'], '2': ['1', '15', '3'], '20': ['123', '21', '27'], '21': ['20', '22', '36'], '22': ['21', '23', '35'], '23': ['22', '24'], '24': ['23', '25'], '25': ['12', '24', '26'], '26': ['11', '25', '27'], '27': ['20', '26', '124'], '3': ['2', '4'], '30': ['133', '31', '37'], '31': ['30', '32', '46'], '32': ['31', '33', '45'], '33': ['32', '34'], '34': ['33', '35'], '35': ['22', '34', '36'], '36': ['21', '35', '37'], '37': ['30', '36', '134'], '4': ['3', '5'], '40': ['143', '41', '47'], '41': ['40', '42'], '42': ['41', '43'], '43': ['42', '44'], '44': ['43', '45'], '45': ['32', '44', '46'], '46': ['31', '45', '47'], '47': ['40', '46', '144'], '5': ['4', '6'], '6': ['5', '7'], '7': ['0', '6', '104']}\n"
- ]
- }
- ],
- "source": [
- "device = AwsDevice(Devices.Rigetti.AspenM3)\n",
- "\n",
- "connectivity_graph = device.properties.paradigm.connectivity.connectivityGraph\n",
- "print(f\"the connectivity of {device.name} is: {connectivity_graph}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "pycharm": {
- "name": "#%% md\n"
- }
- },
- "source": [
- "Next, create a GHZ circuit with three qubits 0, 2, 4, and run it on the Aspen-M-3 device. Notice that none of these qubits are connected on the Aspen-M-3 connectivity graph."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {
- "pycharm": {
- "name": "#%%\n"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "T : |0|1|2|\n",
- " \n",
- "q0 : -H-C-C-\n",
- " | | \n",
- "q2 : ---X-|-\n",
- " | \n",
- "q4 : -----X-\n",
- "\n",
- "T : |0|1|2|\n"
- ]
- }
- ],
- "source": [
- "# create a GHZ state with non-neighboring qubits\n",
- "circuit = Circuit()\n",
- "circuit.h(0).cnot(0,2).cnot(0,4)\n",
- "print(circuit)\n",
- "\n",
- "rigetti_rewiring = device.run(circuit, shots=10)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Status of task: CREATED\n"
- ]
- }
- ],
- "source": [
- "print(\"Status of quantum task:\", rigetti_rewiring.state())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "pycharm": {
- "name": "#%% md\n"
- }
- },
- "source": [
- "To verify the final qubit allocation, retrieve the compiled program that was executed:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {
- "pycharm": {
- "name": "#%%\n"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Measurement counts: Counter({'001': 4, '111': 3, '000': 2, '101': 1})\n",
- "The compiled circuit is:\n",
- " DECLARE ro BIT[3]\n",
- "PRAGMA INITIAL_REWIRING \"PARTIAL\"\n",
- "RESET\n",
- "RZ(-pi/2) 12\n",
- "RX(-pi/2) 12\n",
- "RZ(pi) 13\n",
- "XY(pi) 12 13\n",
- "RZ(pi/2) 12\n",
- "RX(pi/2) 12\n",
- "RZ(-pi/2) 12\n",
- "XY(pi) 12 13\n",
- "RZ(pi/2) 25\n",
- "RX(-pi/2) 25\n",
- "CZ 25 12\n",
- "RZ(pi) 12\n",
- "RX(-pi/2) 13\n",
- "RX(pi/2) 25\n",
- "RZ(-pi/2) 25\n",
- "MEASURE 25 ro[2]\n",
- "MEASURE 13 ro[1]\n",
- "MEASURE 12 ro[0]\n"
- ]
- }
- ],
- "source": [
- "result = rigetti_rewiring.result()\n",
- "counts = result.measurement_counts\n",
- "print(\"Measurement counts:\", counts)\n",
- "print(\"The compiled circuit is:\\n\", result.additional_metadata.rigettiMetadata.compiledProgram)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "pycharm": {
- "name": "#%% md\n"
- }
- },
- "source": [
- "Notice that the PARTIAL rewiring was applied. The qubits 0, 2, 4 in the original circuit were mapped to three other qubits in the Rigetti device, and the gates were compiled into native gates."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## User-defined qubit allocation"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In Amazon Braket, you can choose to prescribe a qubit mapping manually, and prevent further rewiring for Rigetti devices. To enable manual mapping, set `disable_qubit_rewiring=True` when submitting the quantum task to run.\n",
- "\n",
- "If all the gates in the circuit satisfy the topological constraints of the device, Amazon Braket maps abstract qubit $i$ in the circuit to the physical qubit $i$ in the device, and maps qubit pair $(i, j)$ to the connection $(i, j)$ in the device. On the other hand, Amazon Braket raises an exception if a specified qubit or qubit pair do not exist in the device connectivity graph."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {
- "pycharm": {
- "name": "#%%\n"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "T : | 0 |1|\n",
- " \n",
- "q0 : -Rz(1.57)-C-\n",
- " | \n",
- "q1 : ----------X-\n",
- " \n",
- "q3 : -X----------\n",
- "\n",
- "T : | 0 |1|\n"
- ]
- }
- ],
- "source": [
- "# create a random state with neighboring qubits\n",
- "q1=random.choice(list(connectivity_graph))\n",
- "q2=int(connectivity_graph[q1][0])\n",
- "q1=int(q1)\n",
- "\n",
- "circuit = Circuit()\n",
- "circuit.rz(0,np.pi/2).cnot(q1,q2).x(7)\n",
- "print(circuit)\n",
- "rigetti_task = device.run(circuit, shots=10, disable_qubit_rewiring=True)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Status of task: COMPLETED\n"
- ]
- }
- ],
- "source": [
- "print(\"Status of quantum task:\", rigetti_task.state())"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Measurement counts: Counter({'011': 6, '001': 2, '101': 1, '000': 1})\n",
- "The compiled circuit is:\n",
- " DECLARE ro BIT[3]\n",
- "PRAGMA INITIAL_REWIRING \"NAIVE\"\n",
- "RESET\n",
- "RZ(pi) 1\n",
- "RX(-pi/2) 1\n",
- "XY(pi) 0 1\n",
- "RZ(-pi/2) 0\n",
- "RX(pi/2) 0\n",
- "RZ(pi/2) 0\n",
- "XY(pi) 0 1\n",
- "RX(pi) 3\n",
- "MEASURE 3 ro[2]\n",
- "MEASURE 1 ro[1]\n",
- "MEASURE 0 ro[0]\n"
- ]
- }
- ],
- "source": [
- "result = rigetti_task.result()\n",
- "counts = result.measurement_counts\n",
- "print(\"Measurement counts:\", counts)\n",
- "print(\"The compiled circuit is:\\n\", result.additional_metadata.rigettiMetadata.compiledProgram)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "pycharm": {
- "name": "#%% md\n"
- }
- },
- "source": [
- "The qubits in the original circuit followed a one-to-one mapping to the physical qubits in the device. Other compilation steps, such as gate synthesis and circuit optimization, are still performed. These steps allow the circuit to run successfully and improve the overall fidelity."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Using the qubits with the highest two-qubit gate fidelity\n",
- "\n",
- "Additionally, the device properties include calibration data, which you can use to find the qubits and qubit pairs with the highest fidelities for particular gates.\n",
- "\n",
- "The following function finds the qubit pair that has the highest two-qubit fidelity of an input gate, which can be any of the gates native to the Rigetti device, namely CPHASE, XY or CZ."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {
- "pycharm": {
- "name": "#%%\n"
- }
- },
- "outputs": [],
- "source": [
- "def find_qubit_pair(gate):\n",
- " \"Function to find the qubit pair that has the highest gate fidelity of a particular gate\"\n",
- " \n",
- " # check whether the input gate is a string\n",
- " if not isinstance(gate, str):\n",
- " raise ValueError('The input gate must be a string type.') \n",
- " \n",
- " # check whether the input gate is a native gate\n",
- " gate_list = ['CPHASE', 'CZ', 'XY']\n",
- " if gate not in gate_list:\n",
- " raise ValueError('The input gate must be either CPHASE, CZ or XY.')\n",
- " \n",
- " # load all calibration data from device.properties\n",
- " calibration_2Q = device.properties.provider.specs['2Q']\n",
- " highest_fidelity = 0\n",
- "\n",
- " # iterate through all calibration data to find the highest fidelity\n",
- " for pair in calibration_2Q.keys():\n",
- " \n",
- " # if the particular gate type is supported by the qubit pair\n",
- " if ('f'+ gate) in calibration_2Q[pair].keys(): \n",
- " \n",
- " if calibration_2Q[pair]['f'+ gate] > highest_fidelity:\n",
- " \n",
- " # update the highest_fidelity and the best_pair\n",
- " highest_fidelity = calibration_2Q[pair]['f'+ gate]\n",
- " best_pair = pair\n",
- "\n",
- " # generate the two qubits as integers \n",
- " q1 = best_pair[0]\n",
- " i = 1\n",
- " while best_pair[i] is not '-':\n",
- " q1 += best_pair[i]\n",
- " i += 1\n",
- "\n",
- " q1 = int(q1)\n",
- " q2 = int(best_pair[i+1:])\n",
- " \n",
- " return q1, q2, highest_fidelity"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "pycharm": {
- "name": "#%% md\n"
- }
- },
- "source": [
- "The example in the following code applies a native two-qubit gate on the qubit pair that has the highest fidelity of that gate. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {
- "pycharm": {
- "name": "#%%\n"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "The highest fidelity for CZ gate is: 0.9794230141816626\n",
- "And the corresponding qubit pair is: qubit 21 and qubit 36\n",
- "T : |0|\n",
- " \n",
- "q21 : -C-\n",
- " | \n",
- "q36 : -Z-\n",
- "\n",
- "T : |0|\n"
- ]
- }
- ],
- "source": [
- "# the gate must be either 'CZ', 'CPHASE' or 'XY'\n",
- "gate = 'CZ'\n",
- "# find the qubit pair with the highest gate fidelity\n",
- "q1, q2, highest_fidelity = find_qubit_pair(gate)\n",
- "print('The highest fidelity for '+gate+' gate is:', highest_fidelity)\n",
- "print(f'And the corresponding qubit pair is: qubit {q1} and qubit {q2}')\n",
- "\n",
- "# create a circuit with the gate applied to the discovered qubit pair.\n",
- "# note that CPHASE in Rigetti corresponds to cphaseshift in Braket\n",
- "circuit = Circuit()\n",
- "circuit.cz(q1,q2)\n",
- "print(circuit)\n",
- "rigetti_task = device.run(circuit, shots=1000, disable_qubit_rewiring=True)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Measurement counts: Counter({'00': 910, '01': 55, '10': 33, '11': 2})\n",
- "The compiled circuit is:\n",
- " DECLARE ro BIT[2]\n",
- "PRAGMA INITIAL_REWIRING \"NAIVE\"\n",
- "RESET\n",
- "CZ 21 36\n",
- "MEASURE 21 ro[1]\n",
- "MEASURE 36 ro[0]\n"
- ]
- }
- ],
- "source": [
- "print(\"Status of quantum task:\", rigetti_task.state())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The qubits in the original circuit followed a one-to-one mapping to the physical qubits in the device. Since only native gates were used, the actual gates executed are the same as the gates in the original circuit."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "pycharm": {
- "name": "#%% md\n"
- }
- },
- "source": [
- "
\n",
- "Note: The IonQ device does not support manual allocation. For circuits submitted to the IonQ device, qubits are allocated automatically.\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Task Summary\n",
- "{'arn:aws:braket:us-west-1::device/qpu/rigetti/Aspen-M-3': {'shots': 1020, 'tasks': {'QUEUED': 1, 'CREATED': 2}}}\n",
- "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).\n",
- "Estimated cost to run this example: 1.26 USD\n"
- ]
- }
- ],
- "source": [
- "print(\"Quantum Task Summary\")\n",
- "print(t.quantum_tasks_statistics())\n",
- "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).')\n",
- "print(f\"Estimated cost to run this example: {t.qpu_tasks_cost() + t.simulator_tasks_cost():.2f} USD\")"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "conda_braket",
- "language": "python",
- "name": "conda_braket"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.10"
- },
- "vscode": {
- "interpreter": {
- "hash": "590fab68195cf107911461461f81d5c472d3d6127f579badfcfad30f03e5cab2"
- }
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Allocating Qubits on QPU Devices"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Use Braket SDK Cost Tracking to estimate the cost to run this example\n",
+ "from braket.tracking import Tracker\n",
+ "t = Tracker().start()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This notebook demonstrates how you can specify explicitly which qubits to use when you run a quantum circuit on QPU devices from Rigetti.\n",
+ "\n",
+ "When you submit a circuit for execution on a QPU, Amazon Braket performs a series of compilation steps: it maps the _abstract qubits_ in your circuit to _physical qubits_ in the device; it synthesizes gates into the native gate set of the device; it optimizes the circuit to reduce the number of gates; and finally, it translates the gates into executable pulses.\n",
+ "\n",
+ "This section shows how the first step, called qubit allocation, works for the Rigetti Ankaa-2 device."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# general imports\n",
+ "from braket.aws import AwsDevice\n",
+ "from braket.circuits import Circuit\n",
+ "from braket.devices import Devices\n",
+ "import numpy as np\n",
+ "import random"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ },
+ "source": [
+ "## Automatic qubit allocation\n",
+ "\n",
+ "Qubit allocation for Rigetti devices on Amazon Braket utilizes [the Quil Compilers](https://pyquil-docs.rigetti.com/en/latest/compiler.html#the-quil-compiler)'s _rewiring_ strategies. By default, when you submit a circuit on Amazon Braket to a Rigetti device, the circuit is rewired according to the [PARTIAL](https://pyquil-docs.rigetti.com/en/latest/compiler.html#partial) rewiring strategy. Specifically, the compiler starts with an empty mapping from logical to physical qubits. Taking into account the latest calibration data of the device, the compiler fills in the mapping with the goal, sequentially, to maximize the overall fidelity of the circuit.\n",
+ "\n",
+ "The example that follows shows how to create a GHZ state on qubits that are not physically connected. After the quantum task is completed, you can obtain a list of the actual gates executed on the device, by viewing the result metadata.\n",
+ "\n",
+ "First, instantiate the Rigetti Ankaa-2 device and retrieve its connectivity graph, which shows the qubits that are directly connected on the chip."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "the connectivity of Ankaa-2 is: {'0': ['1', '7'], '1': ['0', '2', '8'], '2': ['1', '3', '9'], '3': ['2', '4', '10'], '4': ['3', '5', '11'], '5': ['4', '6', '12'], '6': ['5', '13'], '7': ['0', '8', '14'], '8': ['1', '7', '9', '15'], '9': ['2', '8', '10', '16'], '10': ['3', '9', '11', '17'], '11': ['4', '10', '12', '18'], '12': ['5', '11', '13', '19'], '13': ['6', '12', '20'], '14': ['7', '15', '21'], '15': ['8', '14', '22'], '16': ['9', '17', '23'], '17': ['10', '16', '18', '24'], '18': ['11', '17', '19', '25'], '19': ['12', '18', '20', '26'], '20': ['13', '19', '27'], '21': ['14', '22', '28'], '22': ['15', '21', '23', '29'], '23': ['16', '22', '24', '30'], '24': ['17', '23', '25', '31'], '25': ['18', '24', '26', '32'], '26': ['19', '25', '33'], '27': ['20', '34'], '28': ['21', '29', '35'], '29': ['22', '28', '30', '36'], '30': ['23', '29', '31', '37'], '31': ['24', '30', '32', '38'], '32': ['25', '31', '33', '39'], '33': ['26', '32', '34', '40'], '34': ['27', '33', '41'], '35': ['28', '36', '42'], '36': ['29', '35', '37', '43'], '37': ['30', '36', '38', '44'], '38': ['31', '37', '39', '45'], '39': ['32', '38', '40', '46'], '40': ['33', '39', '41', '47'], '41': ['34', '40', '48'], '42': ['35', '43', '49'], '43': ['36', '42', '44', '50'], '44': ['37', '43', '45', '51'], '45': ['38', '44', '46', '52'], '46': ['39', '45', '47', '53'], '47': ['40', '46', '48', '54'], '48': ['41', '47', '55'], '49': ['42', '56'], '50': ['43', '51', '57'], '51': ['44', '50', '52', '58'], '52': ['45', '51', '53', '59'], '53': ['46', '52', '54'], '54': ['47', '53', '55', '61'], '55': ['48', '54', '62'], '56': ['49', '57', '63'], '57': ['50', '56', '58', '64'], '58': ['51', '57', '59', '65'], '59': ['52', '58', '60', '66'], '60': ['59'], '61': ['54', '62', '68'], '62': ['55', '61', '69'], '63': ['56', '64', '70'], '64': ['57', '63', '65', '71'], '65': ['58', '64', '66', '72'], '66': ['59', '65', '67'], '67': ['66', '68', '74'], '68': ['61', '67', '69', '75'], '69': ['62', '68', '76'], '70': ['63', '71', '77'], '71': ['64', '70', '72', '78'], '72': ['65', '71', '73', '79'], '73': ['72', '74', '80'], '74': ['67', '73', '75', '81'], '75': ['68', '74', '76', '82'], '76': ['69', '75', '83'], '77': ['70', '78'], '78': ['71', '77', '79'], '79': ['72', '78', '80'], '80': ['73', '79', '81'], '81': ['74', '80', '82'], '82': ['75', '81', '83'], '83': ['76', '82']}\n"
+ ]
+ }
+ ],
+ "source": [
+ "device = AwsDevice(Devices.Rigetti.Ankaa2)\n",
+ "\n",
+ "connectivity_graph = device.properties.paradigm.connectivity.connectivityGraph\n",
+ "print(f\"the connectivity of {device.name} is: {connectivity_graph}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ },
+ "source": [
+ "Next, create a GHZ circuit with three qubits 0, 2, 4, and run it on the Ankaa 2 device. Notice that none of these qubits are connected on the Ankaa 2 connectivity graph."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "T : │ 0 │ 1 │ 2 │\n",
+ " ┌───┐ \n",
+ "q0 : ─┤ H ├───●─────●───\n",
+ " └───┘ │ │ \n",
+ " ┌─┴─┐ │ \n",
+ "q2 : ───────┤ X ├───┼───\n",
+ " └───┘ │ \n",
+ " ┌─┴─┐ \n",
+ "q4 : ─────────────┤ X ├─\n",
+ " └───┘ \n",
+ "T : │ 0 │ 1 │ 2 │\n"
+ ]
+ }
+ ],
+ "source": [
+ "# create a GHZ state with non-neighboring qubits\n",
+ "circuit = Circuit()\n",
+ "circuit.h(0).cnot(0,2).cnot(0,4)\n",
+ "print(circuit)\n",
+ "\n",
+ "rigetti_rewiring = device.run(circuit, shots=10)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Status of quantum task: QUEUED\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"Status of quantum task:\", rigetti_rewiring.state())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ },
+ "source": [
+ "To verify the final qubit allocation, retrieve the compiled program that was executed:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Measurement counts: Counter({'111': 6, '000': 3, '101': 1})\n",
+ "The compiled circuit is:\n",
+ " PRAGMA INITIAL_REWIRING \"NAIVE\"\n",
+ "DECLARE ro BIT[3]\n",
+ "PRAGMA PRESERVE_BLOCK\n",
+ "RX(1.5707963267948966) 5\n",
+ "RZ(3.141592653589793) 5\n",
+ "ISWAP 5 12\n",
+ "RZ(1.5707963267948966) 5\n",
+ "RX(1.5707963267948966) 5\n",
+ "RZ(4.71238898038469) 5\n",
+ "ISWAP 5 12\n",
+ "RZ(1.5707963267948966) 5\n",
+ "RZ(3.141592653589793) 12\n",
+ "ISWAP 5 6\n",
+ "RX(1.5707963267948966) 12\n",
+ "RZ(1.5707963267948966) 5\n",
+ "RX(1.5707963267948966) 5\n",
+ "RZ(4.71238898038469) 5\n",
+ "ISWAP 5 6\n",
+ "RZ(3.141592653589793) 6\n",
+ "RX(1.5707963267948966) 6\n",
+ "PRAGMA END_PRESERVE_BLOCK\n",
+ "MEASURE 12 ro[1]\n",
+ "MEASURE 5 ro[0]\n",
+ "MEASURE 6 ro[2]\n"
+ ]
+ }
+ ],
+ "source": [
+ "result = rigetti_rewiring.result()\n",
+ "counts = result.measurement_counts\n",
+ "print(\"Measurement counts:\", counts)\n",
+ "print(\"The compiled circuit is:\\n\", result.additional_metadata.rigettiMetadata.compiledProgram)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ },
+ "source": [
+ "Notice that the PARTIAL rewiring was applied. The qubits 0, 2, 4 in the original circuit were mapped to three other qubits in the Rigetti device, and the gates were compiled into native gates."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## User-defined qubit allocation"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In Amazon Braket, you can choose to prescribe a qubit mapping manually, and prevent further rewiring for Rigetti devices. To enable manual mapping, set `disable_qubit_rewiring=True` when submitting the quantum task to run.\n",
+ "\n",
+ "If all the gates in the circuit satisfy the topological constraints of the device, Amazon Braket maps abstract qubit $i$ in the circuit to the physical qubit $i$ in the device, and maps qubit pair $(i, j)$ to the connection $(i, j)$ in the device. On the other hand, Amazon Braket raises an exception if a specified qubit or qubit pair do not exist in the device connectivity graph."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "T : │ 0 │ 1 │\n",
+ " ┌──────────┐ \n",
+ "q0 : ─┤ Rz(1.57) ├───●───\n",
+ " └──────────┘ │ \n",
+ " ┌─┴─┐ \n",
+ "q1 : ──────────────┤ X ├─\n",
+ " └───┘ \n",
+ " ┌───┐ \n",
+ "q7 : ────┤ X ├───────────\n",
+ " └───┘ \n",
+ "T : │ 0 │ 1 │\n"
+ ]
+ }
+ ],
+ "source": [
+ "# create a random state with neighboring qubits\n",
+ "q1=random.choice(list(connectivity_graph))\n",
+ "q2=int(connectivity_graph[q1][0])\n",
+ "q1=int(q1)\n",
+ "\n",
+ "circuit = Circuit()\n",
+ "circuit.rz(0,np.pi/2).cnot(q1,q2).x(7)\n",
+ "print(circuit)\n",
+ "rigetti_task = device.run(circuit, shots=10, disable_qubit_rewiring=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Status of quantum task: QUEUED\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"Status of quantum task:\", rigetti_task.state())"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Measurement counts: Counter({'001': 7, '000': 2, '011': 1})\n",
+ "The compiled circuit is:\n",
+ " PRAGMA INITIAL_REWIRING \"NAIVE\"\n",
+ "DECLARE ro BIT[3]\n",
+ "PRAGMA PRESERVE_BLOCK\n",
+ "RX(1.5707963267948966) 0\n",
+ "RX(1.5707963267948966) 7\n",
+ "RZ(4.71238898038469) 0\n",
+ "RX(1.5707963267948966) 7\n",
+ "ISWAP 0 1\n",
+ "RZ(3.141592653589793) 1\n",
+ "RX(1.5707963267948966) 1\n",
+ "PRAGMA END_PRESERVE_BLOCK\n",
+ "MEASURE 7 ro[2]\n",
+ "MEASURE 0 ro[0]\n",
+ "MEASURE 1 ro[1]\n"
+ ]
+ }
+ ],
+ "source": [
+ "result = rigetti_task.result()\n",
+ "counts = result.measurement_counts\n",
+ "print(\"Measurement counts:\", counts)\n",
+ "print(\"The compiled circuit is:\\n\", result.additional_metadata.rigettiMetadata.compiledProgram)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ },
+ "source": [
+ "The qubits in the original circuit followed a one-to-one mapping to the physical qubits in the device. Other compilation steps, such as gate synthesis and circuit optimization, are still performed. These steps allow the circuit to run successfully and improve the overall fidelity."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Using the qubits with the highest two-qubit gate fidelity\n",
+ "\n",
+ "Additionally, the device properties include calibration data, which you can use to find the qubits and qubit pairs with the highest fidelities for particular gates.\n",
+ "\n",
+ "The following function finds the qubit pair that has the highest two-qubit fidelity of an input gate, which can be any of the gates native to the Rigetti device. First, you can access the native gates as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['RX', 'RZ', 'CZ', 'ISWAP']"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "native_gates = device.properties.paradigm.nativeGateSet\n",
+ "gates_uppercase = [gate.upper() for gate in native_gates]\n",
+ "gates_uppercase"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def find_qubit_pair(gate):\n",
+ " \"Function to find the qubit pair that has the highest gate fidelity of a particular gate\"\n",
+ " \n",
+ " # check whether the input gate is a string\n",
+ " if not isinstance(gate, str):\n",
+ " raise ValueError('The input gate must be a string type.') \n",
+ " \n",
+ " # check whether the input gate is a native gate\n",
+ " gate_list = gates_uppercase\n",
+ " if gate not in gate_list:\n",
+ " raise ValueError(f'The input gate must be one of {gates_uppercase}.')\n",
+ " \n",
+ " # load all calibration data from device.properties\n",
+ " calibration_2Q = device.properties.provider.specs['2Q']\n",
+ " highest_fidelity = 0\n",
+ "\n",
+ " # iterate through all calibration data to find the highest fidelity\n",
+ " for pair in calibration_2Q.keys():\n",
+ " # if the particular gate type is supported by the qubit pair\n",
+ " if ('f'+ gate) in calibration_2Q[pair].keys(): \n",
+ " if calibration_2Q[pair]['f'+ gate] > highest_fidelity: \n",
+ " # update the highest_fidelity and the best_pair\n",
+ " highest_fidelity = calibration_2Q[pair]['f'+ gate]\n",
+ " best_pair = pair\n",
+ "\n",
+ " # generate the two qubits as integers \n",
+ " q1 = best_pair[0]\n",
+ " i = 1\n",
+ " while best_pair[i] != '-':\n",
+ " q1 += best_pair[i]\n",
+ " i += 1\n",
+ "\n",
+ " q1 = int(q1)\n",
+ " q2 = int(best_pair[i+1:])\n",
+ " \n",
+ " return q1, q2, highest_fidelity"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ },
+ "source": [
+ "The example in the following code applies a native two-qubit gate on the qubit pair that has the highest fidelity of that gate. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The highest fidelity for ISWAP gate is: 0.99312484820244\n",
+ "And the corresponding qubit pair is: qubit 73 and qubit 74\n",
+ "T : │ 0 │\n",
+ " \n",
+ "q73 : ───●───\n",
+ " │ \n",
+ " ┌─┴─┐ \n",
+ "q74 : ─┤ Z ├─\n",
+ " └───┘ \n",
+ "T : │ 0 │\n"
+ ]
+ }
+ ],
+ "source": [
+ "# the gate must be a native gate\n",
+ "gate = 'ISWAP'\n",
+ "# find the qubit pair with the highest gate fidelity\n",
+ "q1, q2, highest_fidelity = find_qubit_pair(gate)\n",
+ "print('The highest fidelity for '+gate+' gate is:', highest_fidelity)\n",
+ "print(f'And the corresponding qubit pair is: qubit {q1} and qubit {q2}')\n",
+ "\n",
+ "# create a circuit with the gate applied to the discovered qubit pair.\n",
+ "# note that CPHASE in Rigetti corresponds to cphaseshift in Braket\n",
+ "circuit = Circuit()\n",
+ "circuit.cz(q1,q2)\n",
+ "print(circuit)\n",
+ "rigetti_task = device.run(circuit, shots=1000, disable_qubit_rewiring=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Status of quantum task: QUEUED\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"Status of quantum task:\", rigetti_task.state())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The qubits in the original circuit followed a one-to-one mapping to the physical qubits in the device. Since only native gates were used, the actual gates executed are the same as the gates in the original circuit."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ },
+ "source": [
+ "
\n",
+ "Note: The IonQ device does not support manual allocation. For circuits submitted to the IonQ device, qubits are allocated automatically.\n",
+ "
\n",
+ " Note: This section and the next verbatim box section uses the Rigetti Ankaa-2 device. When you run this notebook, make sure the device is currently available. You can find QPU availability windows on the Devices page in the Amazon Braket Console\n",
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import networkx as nx\n",
+ "# access and visualize the device topology\n",
+ "print(rigetti.properties.paradigm.connectivity.connectivityGraph)\n",
+ "nx.draw_kamada_kawai(rigetti.topology_graph, with_labels=True, font_color=\"white\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9fa09f83a0747394",
+ "metadata": {},
+ "source": [
+ "Now we can submit a quantum task of the above program with verbatim box."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "id": "b5e7c89c1c9f8879",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2023-11-21T08:32:56.299544Z",
+ "start_time": "2023-11-21T08:32:56.297194Z"
+ },
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "PRAGMA INITIAL_REWIRING \"NAIVE\"\n",
+ "DECLARE ro BIT[2]\n",
+ "PRAGMA PRESERVE_BLOCK\n",
+ "RX(3.141592653589793) 0\n",
+ "RX(3.141592653589793) 0\n",
+ "ISWAP 0 1\n",
+ "PRAGMA END_PRESERVE_BLOCK\n",
+ "MEASURE 0 ro[0]\n",
+ "MEASURE 1 ro[1]\n"
+ ]
+ }
+ ],
+ "source": [
+ "verbatim_task = rigetti.run(OpenQASMProgram(source=program_with_verbatim_box), shots = 10)\n",
+ "verbatim_result = verbatim_task.result()\n",
+ "meta = verbatim_result.additional_metadata.rigettiMetadata\n",
+ "print(meta.compiledProgram)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "bcd27bc239db889d",
+ "metadata": {},
+ "source": [
+ "As shown above, the two consecutive `rx` $\\pi$-rotation gates did not get optimized and we confirm that our program was indeed executed verbatim."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b4768a96f7b1b3b6",
+ "metadata": {},
+ "source": [
+ "## Requesting Result Types with OpenQASM\n",
+ "\n",
+ "Braket provides [a rich library of result types](https://docs.aws.amazon.com/braket/latest/developerguide/braket-result-types.html) for circuit executions. With OpenQASM, requesting different result types for our quantum tasks is easier than ever using the `result` pragma. Next, we give an example of requesting result types for our Bell state program submitted to SV1. Before doing that, let's see what result types are supported on SV1:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "id": "b17d21060cda7fa7",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2023-11-21T08:32:56.304110Z",
+ "start_time": "2023-11-21T08:32:56.300283Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "name='Sample' observables=['x', 'y', 'z', 'h', 'i', 'hermitian'] minShots=1 maxShots=100000\n",
+ "name='Expectation' observables=['x', 'y', 'z', 'h', 'i', 'hermitian'] minShots=0 maxShots=100000\n",
+ "name='Variance' observables=['x', 'y', 'z', 'h', 'i', 'hermitian'] minShots=0 maxShots=100000\n",
+ "name='Probability' observables=None minShots=1 maxShots=100000\n",
+ "name='Amplitude' observables=None minShots=0 maxShots=0\n",
+ "name='AdjointGradient' observables=['x', 'y', 'z', 'h', 'i'] minShots=0 maxShots=0\n"
+ ]
+ }
+ ],
+ "source": [
+ "# print the result types supported by SV1\n",
+ "for iter in sv1.properties.action['braket.ir.openqasm.program'].supportedResultTypes:\n",
+ " print(iter)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "bbc02495939691c9",
+ "metadata": {},
+ "source": [
+ "With knowing the supported result types on SV1, we choose to request the `Expectation` of $X \\otimes Z$ observable on `q[0]` and `q[1]` and the `Amplitude` result type for a `shots=0` quantum task of our bell program:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "id": "6e3b521b7356b4bd",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2023-11-21T08:32:56.305634Z",
+ "start_time": "2023-11-21T08:32:56.302835Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "bell_with_result_type = \"\"\"\n",
+ "OPENQASM 3;\n",
+ "\n",
+ "qubit[2] q;\n",
+ "\n",
+ "#pragma braket result expectation x(q[0]) @ z(q[1])\n",
+ "#pragma braket result amplitude \"00\", \"11\"\n",
+ "h q[0];\n",
+ "cnot q[0], q[1];\n",
+ "\"\"\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6435b34ab2525039",
+ "metadata": {},
+ "source": [
+ "The location of the `result` pragma is very flexible as long as it's after the qubit register definition (if you use physical qubits, you can put `result` pragmas anywhere after the program header).\n",
+ "\n",
+ "We can submit the above program and receive the results for our requested result types."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "id": "a42b0ebc71b59136",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2023-11-21T08:32:59.113501Z",
+ "start_time": "2023-11-21T08:32:56.306452Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[0.0, {'00': (0.7071067811865475+0j), '11': (0.7071067811865475+0j)}]\n"
+ ]
+ }
+ ],
+ "source": [
+ "bell_result_types_task = sv1.run(OpenQASMProgram(source=bell_with_result_type), shots = 0)\n",
+ "bell_result = bell_result_types_task.result()\n",
+ "values = bell_result.values\n",
+ "print(values)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "38675f5678613895",
+ "metadata": {},
+ "source": [
+ "At last, we want to remind our Braket OpenQASM users that there are two requirements when requesting result types:\n",
+ "1. For `shots=0` quantum tasks, requesting non-simultaneously measurable result types is allowed, but for `shots>0` quantum tasks, it is not allowed. For example, we can write the following OpenQASM program in a `shots=0` quantum task but not in a `shots>0` quantum task, since the two result types are not simultaneously measurable:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "id": "45277f89209faec7",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2023-11-21T08:32:59.115339Z",
+ "start_time": "2023-11-21T08:32:59.112696Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "program_with_non_simultaneously_measurable_result_types = \"\"\"\n",
+ "OPENQASM 3;\n",
+ "\n",
+ "qubit[2] q;\n",
+ "\n",
+ "h q[0];\n",
+ "cnot q[0], q[1];\n",
+ "\n",
+ "#pragma braket result expectation x(q[0]) @ z(q[1])\n",
+ "#pragma braket result expectation hermitian([[0, -1im], [1im, 0]]) q[0]\n",
+ "\"\"\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a9af8819fba31a90",
+ "metadata": {},
+ "source": [
+ "2. Do not use measurement instructions and request result types in the same OpenQASM program, otherwise a validation error will be raised. Since measurement instructions are basically equivalent to `#pragma braket result sample z(qubit)`, we encourage users to adapt a consistent style of requesting result types in the same program."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "db79ad324a5f6478",
+ "metadata": {
+ "collapsed": false
+ },
+ "source": [
+ "Circuits constructed with `from_ir` will have the correct result types:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "id": "f3c438da3a351153",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2023-11-21T08:32:59.127212Z",
+ "start_time": "2023-11-21T08:32:59.124909Z"
+ },
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "bell_with_result_type:\n",
+ "T : │ 0 │ 1 │ Result Types │\n",
+ " ┌───┐ ┌──────────────────┐ \n",
+ "q0 : ─┤ H ├───●───┤ Expectation(X@Z) ├─\n",
+ " └───┘ │ └────────┬─────────┘ \n",
+ " ┌─┴─┐ ┌────────┴─────────┐ \n",
+ "q1 : ───────┤ X ├─┤ Expectation(X@Z) ├─\n",
+ " └───┘ └──────────────────┘ \n",
+ "T : │ 0 │ 1 │ Result Types │\n",
+ "\n",
+ "Additional result types: Amplitude(00,11)\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"bell_with_result_type:\")\n",
+ "print(Circuit.from_ir(bell_with_result_type))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c371de3097c0598a",
+ "metadata": {
+ "collapsed": false
+ },
+ "source": [
+ "## Advanced OpenQASM features\n",
+ "\n",
+ "OpenQASM has features beyond what is natively supported by the `Circuit` class. You can run OpenQASM tasks directly on the Local Simulator or use Circuit.from_ir to convert an OpenQASM program to a Circuit object, which is supported by all circuit-based Braket devices. The following OpenQASM program is a GHZ state written with classical control flow and subroutines:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "id": "5acb72003cd0a0f5",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2023-12-04T04:04:08.355878Z",
+ "start_time": "2023-12-04T04:04:08.353679Z"
+ },
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "ghz_with_advanced_features = \"\"\"\n",
+ "OPENQASM 3.0;\n",
+ "\n",
+ "def ghz(int[32] n) {\n",
+ " h q[0];\n",
+ " for int i in [0:n - 1] {\n",
+ " cnot q[i], q[i + 1];\n",
+ " }\n",
+ "}\n",
+ "\n",
+ "int[32] n = 5;\n",
+ "bit[n + 1] c;\n",
+ "qubit[n + 1] q;\n",
+ "\n",
+ "ghz(n);\n",
+ "\n",
+ "c = measure q;\n",
+ "\"\"\"\n",
+ "qasm_program = OpenQASMProgram(source=ghz_with_advanced_features)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f5d32bd10c64543",
+ "metadata": {
+ "collapsed": false
+ },
+ "source": [
+ "The local simulator supports these features:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "id": "c82bee530da348f6",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2023-12-04T04:04:13.932270Z",
+ "start_time": "2023-12-04T04:04:13.863203Z"
+ },
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "This program uses OpenQASM language features that may not be supported on QPUs or on-demand simulators.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "Counter({'000000': 505, '111111': 495})"
+ ]
+ },
+ "execution_count": 40,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from braket.devices import LocalSimulator\n",
+ "LocalSimulator().run(qasm_program, shots=1000).result().measurement_counts"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "96fca192b4f51797",
+ "metadata": {
+ "collapsed": false
+ },
+ "source": [
+ "but SV1 does not. However, `from_ir` will \"unroll\" the subroutine and loop to create a `Circuit` object that _can_ run on SV1:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "id": "b0886dfc74978bfa",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2023-12-04T04:04:19.974386Z",
+ "start_time": "2023-12-04T04:04:16.705969Z"
+ },
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "This program uses OpenQASM language features that may not be supported on QPUs or on-demand simulators.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │\n",
+ " ┌───┐ ┌───┐ \n",
+ "q0 : ─┤ H ├───●───────────────────────────┤ M ├─\n",
+ " └───┘ │ └───┘ \n",
+ " ┌─┴─┐ ┌───┐ \n",
+ "q1 : ───────┤ X ├───●─────────────────────┤ M ├─\n",
+ " └───┘ │ └───┘ \n",
+ " ┌─┴─┐ ┌───┐ \n",
+ "q2 : ─────────────┤ X ├───●───────────────┤ M ├─\n",
+ " └───┘ │ └───┘ \n",
+ " ┌─┴─┐ ┌───┐ \n",
+ "q3 : ───────────────────┤ X ├───●─────────┤ M ├─\n",
+ " └───┘ │ └───┘ \n",
+ " ┌─┴─┐ ┌───┐ \n",
+ "q4 : ─────────────────────────┤ X ├───●───┤ M ├─\n",
+ " └───┘ │ └───┘ \n",
+ " ┌─┴─┐ ┌───┐ \n",
+ "q5 : ───────────────────────────────┤ X ├─┤ M ├─\n",
+ " └───┘ └───┘ \n",
+ "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │\n",
+ "Counter({'000000': 500, '111111': 500})\n"
+ ]
+ }
+ ],
+ "source": [
+ "circuit = Circuit.from_ir(qasm_program)\n",
+ "print(circuit)\n",
+ "print(sv1.run(circuit, shots=1000).result().measurement_counts)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a329fa3df47ffee4",
+ "metadata": {
+ "collapsed": false
+ },
+ "source": [
+ "For an in-depth exploration of advanced OpenQASM features, see [Simulating Advanced OpenQASM Programs with the Local Simulator](https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/braket_features/Simulating_Advanced_OpenQASM_Programs_with_the_Local_Simulator.ipynb)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9e4c7a48a2b83ce1",
+ "metadata": {},
+ "source": [
+ "# Conclusion\n",
+ "\n",
+ "In this notebook, you learned how to submit OpenQASM quantum tasks and use OpenQASM features on Braket. Hope you enjoyed it! You can find more information about OpenQASM3.0 in its [live specification](https://openqasm.com/), and you can learn more about OpenQASM support on Braket in the [Amazon Braket documentation](https://docs.aws.amazon.com/braket/latest/developerguide/)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "id": "3f5bc3dfad520f14",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2023-11-21T08:32:59.624204Z",
+ "start_time": "2023-11-21T08:32:59.127772Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Quantum Task Summary\n",
+ "{<_Amazon.SV1: 'arn:aws:braket:::device/quantum-simulator/amazon/sv1'>: {'shots': 1120, 'tasks': {'CREATED': 1, 'COMPLETED': 4}, 'execution_duration': datetime.timedelta(microseconds=104000), 'billed_execution_duration': datetime.timedelta(seconds=12)}, <_Amazon.DM1: 'arn:aws:braket:::device/quantum-simulator/amazon/dm1'>: {'shots': 30, 'tasks': {'COMPLETED': 3}, 'execution_duration': datetime.timedelta(microseconds=39000), 'billed_execution_duration': datetime.timedelta(seconds=9)}, <_Rigetti.Ankaa2: 'arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-2'>: {'shots': 20, 'tasks': {'COMPLETED': 2}}}\n",
+ "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).\n",
+ "Estimated cost to run this example: 0.64 USD\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(\"Quantum Task Summary\")\n",
+ "print(t.quantum_tasks_statistics())\n",
+ "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).')\n",
+ "print(f\"Estimated cost to run this example: {t.qpu_tasks_cost() + t.simulator_tasks_cost():.2f} USD\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.6"
+ }
},
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "from braket.ir.openqasm import Program as OpenQASMProgram\n",
- "\n",
- "bell_program = OpenQASMProgram(source=bell_qasm)\n",
- "bell_task = sv1.run(\n",
- " bell_program, \n",
- " shots=100, \n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ba9729ce6ab2bf91",
- "metadata": {},
- "source": [
- "### Submit OpenQASM 3.0 programs using the AWS Command Line Interface\n",
- "\n",
- "Alternatively, if you like the command line experience or you are not a Python user, you can also choose to use the [AWS Command Line Interface (CLI)](https://aws.amazon.com/cli/) to submit our Bell state program. Before doing that we have to make sure we have [AWS CLI installed](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). The following code saves the `bell_qasm` string to a file named `bell.qasm`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "61362f489016dd1f",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:41.407834Z",
- "start_time": "2023-11-21T08:32:41.403524Z"
- }
- },
- "outputs": [],
- "source": [
- "with open(\"bell.qasm\", \"w\") as f:\n",
- " f.write(bell_qasm)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "5c4be932b51fd2ac",
- "metadata": {},
- "source": [
- "Then we can use the command below to submit the quantum task via AWS CLI. Remember to replace the placeholder \\\"amazon-braket-my-bucket\\\" with your own bucket name.\n",
- " \n",
- " aws braket create-quantum-task \\\n",
- " --region \"us-west-1\" \\\n",
- " --device-arn \"arn:aws:braket:us-west-1::device/qpu/rigetti/Aspen-M-3\" \\\n",
- " --shots 100 \\\n",
- " --action '{\n",
- " \"braketSchemaHeader\": {\n",
- " \"name\": \"braket.ir.openqasm.program\", \n",
- " \"version\": \"1\"\n",
- " },\n",
- " \"source\": $(cat bell.qasm)\n",
- " }'"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "68222e9240c91d58",
- "metadata": {
- "collapsed": false
- },
- "source": [
- "## Convert OpenQASM 3.0 programs to circuits\n",
- "\n",
- "You can convert OpenQASM programs into Braket `Circuit` objects if you want to programmatically change your program."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "id": "96417df481e2f5d",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:41.420933Z",
- "start_time": "2023-11-21T08:32:41.407658Z"
- },
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "T : |0|1|\n",
- " \n",
- "q0 : -H-C-\n",
- " | \n",
- "q1 : ---X-\n",
- "\n",
- "T : |0|1|\n"
- ]
- }
- ],
- "source": [
- "from braket.circuits import Circuit\n",
- "print(Circuit.from_ir(bell_qasm))"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "4fec44c2bb4680df",
- "metadata": {},
- "source": [
- "## Figure out what OpenQASM features are supported on each device\n",
- "\n",
- "Different devices on Braket support different subsets of OpenQASM features. To see what are the supported OpenQASM features on each device, we can simply check the device capability for OpenQASM actions. As an example, we can take a look at the `action` field in the device capability of SV1 simulator:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "id": "78a5c9f03581cea4",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:41.425542Z",
- "start_time": "2023-11-21T08:32:41.422663Z"
- }
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "['ccnot',\n",
- " 'cnot',\n",
- " 'cphaseshift',\n",
- " 'cphaseshift00',\n",
- " 'cphaseshift01',\n",
- " 'cphaseshift10',\n",
- " 'cswap',\n",
- " 'cy',\n",
- " 'cz',\n",
- " 'ecr',\n",
- " 'h',\n",
- " 'i',\n",
- " 'iswap',\n",
- " 'pswap',\n",
- " 'phaseshift',\n",
- " 'rx',\n",
- " 'ry',\n",
- " 'rz',\n",
- " 's',\n",
- " 'si',\n",
- " 'swap',\n",
- " 't',\n",
- " 'ti',\n",
- " 'v',\n",
- " 'vi',\n",
- " 'x',\n",
- " 'xx',\n",
- " 'xy',\n",
- " 'y',\n",
- " 'yy',\n",
- " 'z',\n",
- " 'zz',\n",
- " 'gpi',\n",
- " 'gpi2',\n",
- " 'ms']"
- ]
- },
- "execution_count": 9,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# supportedOperations\n",
- "sv1.properties.action['braket.ir.openqasm.program'].supportedOperations"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "id": "85fbc40dc254813e",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:41.463833Z",
- "start_time": "2023-11-21T08:32:41.425725Z"
- }
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "['braket_unitary_matrix',\n",
- " 'braket_basis_rotation',\n",
- " 'braket_result_type_sample',\n",
- " 'braket_result_type_expectation',\n",
- " 'braket_result_type_variance',\n",
- " 'braket_result_type_probability',\n",
- " 'braket_result_type_amplitude',\n",
- " 'braket_result_type_adjoint_gradient']"
- ]
- },
- "execution_count": 10,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# supportedPragmas\n",
- "sv1.properties.action['braket.ir.openqasm.program'].supportedPragmas"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "id": "1fa721d9960f5412",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:41.464559Z",
- "start_time": "2023-11-21T08:32:41.429030Z"
- }
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "['braket_result_type_state_vector',\n",
- " 'braket_result_type_density_matrix',\n",
- " 'braket_noise_amplitude_damping',\n",
- " 'braket_noise_bit_flip',\n",
- " 'braket_noise_depolarizing',\n",
- " 'braket_noise_kraus',\n",
- " 'braket_noise_pauli_channel',\n",
- " 'braket_noise_generalized_amplitude_damping',\n",
- " 'braket_noise_phase_flip',\n",
- " 'braket_noise_phase_damping',\n",
- " 'braket_noise_two_qubit_dephasing',\n",
- " 'braket_noise_two_qubit_depolarizing']"
- ]
- },
- "execution_count": 11,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# forbiddenPragmas\n",
- "sv1.properties.action['braket.ir.openqasm.program'].forbiddenPragmas"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "8190bb8089c7db3f",
- "metadata": {},
- "source": [
- "The SV1 OpenQASM `action` field lists supported/forbidden OpenQASM features on the device, including `supportedPragmas`, `forbiddenPragmas`, `maximumQubitArrays`, `maximumClassicalArrays`, `requiresAllQubitsMeasurement`, `supportedResultTypes`, etc. The names are self-evident, but readers are encouraged to visit the [Amazon Braket developer guide](https://docs.aws.amazon.com/braket/latest/developerguide/braket-using.html) for full information of what these fields mean."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ee537a4601e4c244",
- "metadata": {},
- "source": [
- "# OpenQASM features on Braket\n",
- "\n",
- "Braket supports many useful OpenQASM features, either through the OpenQASM program syntax or Braket-specific pragmas. We will walk through some of these features in this section.\n",
- "\n",
- "## Simulating Noise with OpenQASM\n",
- "\n",
- "With the fully on-demand, high-performance, density-matrix simulator [DM1](https://docs.aws.amazon.com/braket/latest/developerguide/braket-devices.html#braket-simulator-dm1), you can easily investigate the effects of realistic noise on your quantum programs. Now, we show how to use OpenQASM programs to leverage the circuit-level noise simulation capability of DM1.\n",
- "\n",
- "To simulate noise, we have to be able to specify different noise channels. Although syntax for noise channels is not available in the OpenQASM language, Braket uses the `pragma` statement to extend OpenQASM for defining noise channels. Here is an example of an OpenQASM program that prepares a noisy 3-qubit [GHZ state](https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state):"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "id": "4d8a62cfc51a18b",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:41.464621Z",
- "start_time": "2023-11-21T08:32:41.431447Z"
- }
- },
- "outputs": [],
- "source": [
- "noisy_ghz3_program = \"\"\"\n",
- "// noisy_ghz3.qasm\n",
- "// Prepare a 3 noisy qubit GHZ state\n",
- "OPENQASM 3;\n",
- "\n",
- "qubit[3] q;\n",
- "bit[3] c;\n",
- "\n",
- "h q[0];\n",
- "#pragma braket noise depolarizing(0.1) q[0]\n",
- "cnot q[0], q[1];\n",
- "#pragma braket noise depolarizing(0.1) q[0]\n",
- "#pragma braket noise depolarizing(0.1) q[1]\n",
- "cnot q[1], q[2];\n",
- "#pragma braket noise depolarizing(0.1) q[0]\n",
- "#pragma braket noise depolarizing(0.1) q[1]\n",
- "\n",
- "c = measure q;\n",
- "\"\"\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3a988726bb4c39e8",
- "metadata": {},
- "source": [
- "In the example above, we inserted the depolarizing noise channel with probability of 0.1 after each gate in the circuit. The `noisy_ghz3_program` is equivalent to the following program in the Braket SDK:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "id": "647e3717ccbba672",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:41.464801Z",
- "start_time": "2023-11-21T08:32:41.434396Z"
- }
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "Circuit('instructions': [Instruction('operator': H('qubit_count': 1), 'target': QubitSet([Qubit(0)]), 'control': QubitSet([]), 'control_state': (), 'power': 1), Instruction('operator': Depolarizing(0.1), 'target': QubitSet([Qubit(0)]), 'control': QubitSet([]), 'control_state': (), 'power': 1), Instruction('operator': CNot('qubit_count': 2), 'target': QubitSet([Qubit(0), Qubit(1)]), 'control': QubitSet([]), 'control_state': (), 'power': 1), Instruction('operator': Depolarizing(0.1), 'target': QubitSet([Qubit(0)]), 'control': QubitSet([]), 'control_state': (), 'power': 1), Instruction('operator': Depolarizing(0.1), 'target': QubitSet([Qubit(1)]), 'control': QubitSet([]), 'control_state': (), 'power': 1), Instruction('operator': CNot('qubit_count': 2), 'target': QubitSet([Qubit(1), Qubit(2)]), 'control': QubitSet([]), 'control_state': (), 'power': 1), Instruction('operator': Depolarizing(0.1), 'target': QubitSet([Qubit(1)]), 'control': QubitSet([]), 'control_state': (), 'power': 1), Instruction('operator': Depolarizing(0.1), 'target': QubitSet([Qubit(2)]), 'control': QubitSet([]), 'control_state': (), 'power': 1)])"
- ]
- },
- "execution_count": 13,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "from braket.circuits import Circuit, noises\n",
- "\n",
- "noisy_ghz3_circ = Circuit().h(0).cnot(0, 1).cnot(1, 2)\n",
- "noise = noises.Depolarizing(probability=0.1)\n",
- "noisy_ghz3_circ.apply_gate_noise(noise)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "1db047fbac6a26bb",
- "metadata": {},
- "source": [
- "To see if `noisy_ghz3_program` and `noisy_ghz3_circ` are indeed the same, we can run both circuits and compare the results:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "id": "4c57de26c172aa5",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:42.963722Z",
- "start_time": "2023-11-21T08:32:41.441098Z"
- }
- },
- "outputs": [],
- "source": [
- "dm1 = AwsDevice(Devices.Amazon.DM1)\n",
- "\n",
- "noisy_ghz3_circ_task = dm1.run(noisy_ghz3_circ, shots = 10)\n",
- "noisy_ghz3_program_task = dm1.run(OpenQASMProgram(source=noisy_ghz3_program), shots = 10)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "id": "75984e0e95dc4afb",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:45.959888Z",
- "start_time": "2023-11-21T08:32:42.975561Z"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "sdk measurement results: Counter({'111': 5, '000': 4, '100': 1})\n",
- "openqasm measurement results: Counter({'000': 5, '111': 4, '100': 1})\n"
- ]
- }
- ],
- "source": [
- "sdk_result = noisy_ghz3_circ_task.result()\n",
- "openqasm_result = noisy_ghz3_program_task.result()\n",
- "sdk_measurement = sdk_result.measurement_counts\n",
- "openqasm_measurement = openqasm_result.measurement_counts\n",
- "print('sdk measurement results:', sdk_measurement)\n",
- "print('openqasm measurement results:', openqasm_measurement)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f2720672ca96b104",
- "metadata": {},
- "source": [
- "As expected, the measurement counts of the two are very close.\n",
- "\n",
- "In addition to depolarizing noises, we can simulate more complicated noise types with Braket, e.g., `pauli_channel`, `amplitude_damping`, etc. Check the [Amazon Braket developer guide](https://docs.aws.amazon.com/braket/latest/developerguide/braket-using.html) for a complete list of noise channels supported on Braket. Here we give another example of general noise channels defined by the Kraus representation."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "id": "f3231a7767d20640",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:45.971122Z",
- "start_time": "2023-11-21T08:32:45.960859Z"
- }
- },
- "outputs": [],
- "source": [
- "noisy_program_with_kraus_operators = \"\"\"\n",
- "// noisy_program_with_kraus_operators\n",
- "OPENQASM 3;\n",
- "\n",
- "qubit[2] q;\n",
- "bit[2] c;\n",
- "\n",
- "h q[0];\n",
- "#pragma braket noise kraus([[0.9486833, 0], [0, 0.9486833]], [[0, 0.31622777], [0.31622777, 0]]) q[0]\n",
- "cnot q[0], q[1];\n",
- "\n",
- "c = measure q;\n",
- "\"\"\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ebac8bbb6d45341d",
- "metadata": {},
- "source": [
- "We inserted a single qubit noise channel defined by two 2x2 complex Kraus operators in the example above on qubit `q[0]`. Braket will validate if the Kraus operators indeed form a Completely-Positive and Trace-Preserving (CPTP) map."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ffa5e09c2ceab140",
- "metadata": {
- "collapsed": false
- },
- "source": [
- "The `from_ir` method supports noise operations, including general Kraus operators."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "id": "69c7ae1c396ac705",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:46.004276Z",
- "start_time": "2023-11-21T08:32:45.977541Z"
- },
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "noisy_ghz3_program:\n",
- "T : | 0 | 1 | 2 |\n",
- " \n",
- "q0 : -H-DEPO(0.1)-C-DEPO(0.1)-DEPO(0.1)-------------\n",
- " | \n",
- "q1 : -------------X-DEPO(0.1)-----------C-DEPO(0.1)-\n",
- " | \n",
- "q2 : -----------------------------------X-----------\n",
- "\n",
- "T : | 0 | 1 | 2 |\n",
- "\n",
- "noisy_program_with_kraus_operators:\n",
- "T : | 0 |1|\n",
- " \n",
- "q0 : -H-KR-C-\n",
- " | \n",
- "q1 : ------X-\n",
- "\n",
- "T : | 0 |1|\n",
- "\n",
- "Kraus operators:\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "[array([[0.9486833+0.j, 0. +0.j],\n",
- " [0. +0.j, 0.9486833+0.j]]),\n",
- " array([[0. +0.j, 0.31622777+0.j],\n",
- " [0.31622777+0.j, 0. +0.j]])]"
- ]
- },
- "execution_count": 16,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "print(\"noisy_ghz3_program:\")\n",
- "print(Circuit.from_ir(noisy_ghz3_program))\n",
- "print()\n",
- "print(\"noisy_program_with_kraus_operators:\")\n",
- "circuit_kraus = Circuit.from_ir(noisy_program_with_kraus_operators)\n",
- "print(circuit_kraus)\n",
- "print()\n",
- "print(\"Kraus operators:\")\n",
- "circuit_kraus.instructions[1].operator.to_matrix()"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a6b1ce4e1900550",
- "metadata": {},
- "source": [
- "## Submitting parametrized quantum tasks with OpenQASM\n",
- "\n",
- "The on-demand [SV1 simulator](https://docs.aws.amazon.com/braket/latest/developerguide/braket-devices.html#braket-simulator-sv1) and [DM1 simulator](https://docs.aws.amazon.com/braket/latest/developerguide/braket-devices.html#braket-simulator-dm1) support submitting OpenQASM programs with free parameters. You can set the value of the parameter when you submit the quantum task, like so:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "id": "71245a134d32fa26",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:46.007177Z",
- "start_time": "2023-11-21T08:32:46.003030Z"
- }
- },
- "outputs": [],
- "source": [
- "parameter_qasm = \"\"\"\n",
- "OPENQASM 3.0;\n",
- "input float alpha;\n",
- "\n",
- "bit[2] b;\n",
- "qubit[2] q;\n",
- "\n",
- "h q[0];\n",
- "h q[1];\n",
- "rx(alpha) q[0];\n",
- "rx(alpha) q[1];\n",
- "b[0] = measure q[0];\n",
- "b[1] = measure q[1];\n",
- "\"\"\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "bec130ddc2409db5",
- "metadata": {},
- "source": [
- "The `input float alpha` line indicates that we have an input parameter of type `float` named `alpha`. We can specify a value for `alpha` when we submit the quantum task using the optional `inputs` argument to `run`. `input` should be a `dict` of `string`-`float` pairs."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "id": "b831353ac6cfab2a",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:48.743543Z",
- "start_time": "2023-11-21T08:32:46.007096Z"
- },
- "scrolled": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Counter({'00': 4, '01': 3, '11': 3})\n"
- ]
- }
- ],
- "source": [
- "input_dict = {'alpha': 0.1}\n",
- "param_sv1_task = sv1.run(OpenQASMProgram(source=parameter_qasm), shots = 10, inputs=input_dict)\n",
- "param_sv1_result = param_sv1_task.result()\n",
- "print(param_sv1_result.measurement_counts)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "eb90a29d1debc7b4",
- "metadata": {},
- "source": [
- "Similarly, we can specify values for parametrized noise operations:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "id": "94f2688b3380c621",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:48.746739Z",
- "start_time": "2023-11-21T08:32:48.743876Z"
- }
- },
- "outputs": [],
- "source": [
- "parameter_noise_qasm = \"\"\"\n",
- "OPENQASM 3.0;\n",
- "input float beta;\n",
- "input float alpha;\n",
- "bit[2] b;\n",
- "qubit[2] q;\n",
- "h q[0];\n",
- "h q[1];\n",
- "rx(alpha) q[0];\n",
- "rx(alpha) q[1];\n",
- "#pragma braket noise bit_flip(beta) q[0]\n",
- "b[0] = measure q[0];\n",
- "b[1] = measure q[1];\n",
- "\"\"\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "101d366dca84ddb2",
- "metadata": {},
- "source": [
- "We can see there are now two `input` lines, one for each free parameter."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "id": "a0aa8a773b16dc22",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:51.579287Z",
- "start_time": "2023-11-21T08:32:48.747668Z"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Counter({'00': 3, '01': 3, '11': 2, '10': 2})\n"
- ]
- }
- ],
- "source": [
- "noise_input_dict = {'alpha': 0.1, 'beta': 0.2}\n",
- "param_dm1_task = dm1.run(OpenQASMProgram(source=parameter_qasm), shots = 10, inputs=noise_input_dict)\n",
- "param_dm1_result = param_dm1_task.result()\n",
- "print(param_dm1_result.measurement_counts)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "7e60112ddc974a94",
- "metadata": {},
- "source": [
- "## Simulating arbitrary unitaries with OpenQASM\n",
- "\n",
- "The on-demand [SV1 simulator](https://docs.aws.amazon.com/braket/latest/developerguide/braket-devices.html#braket-simulator-sv1) allows us to simulate arbitrary unitary gates in a circuit. With OpenQASM, we can use the `unitary` pramga to insert these arbitrary unitary gates: "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "id": "ffc32efec9315f0",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:51.589214Z",
- "start_time": "2023-11-21T08:32:51.579008Z"
- }
- },
- "outputs": [],
- "source": [
- "program_with_unitary = \"\"\"\n",
- "// noisy_program_with_kraus_operators\n",
- "OPENQASM 3;\n",
- "\n",
- "qubit q;\n",
- "bit c;\n",
- "\n",
- "#pragma braket unitary([[0, -1im], [1im, 0]]) q\n",
- "c = measure q;\n",
- "\"\"\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "8c190474a6e28563",
- "metadata": {},
- "source": [
- "The `1im` in the `unitary` pragma is the OpenQASM notation of the imaginary number $i$, thus, we were simply using the pragma to perform a Pauli Y gate. We can check it by submitting the above program to SV1."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "id": "6d687471d81b2367",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:54.390589Z",
- "start_time": "2023-11-21T08:32:51.586164Z"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Counter({'1': 10})\n"
- ]
- }
- ],
- "source": [
- "unitary_task = sv1.run(OpenQASMProgram(source=program_with_unitary), shots = 10)\n",
- "unitary_result = unitary_task.result()\n",
- "print(unitary_result.measurement_counts)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "76f2897ec744dafb",
- "metadata": {},
- "source": [
- "As expected, the Pauli Y gate flipped the initial 0 state to the 1 state."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "111ead44155005a8",
- "metadata": {
- "collapsed": false
- },
- "source": [
- "`from_ir` will reconstruct the unitary:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "id": "7b631be4b6785b90",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:54.403015Z",
- "start_time": "2023-11-21T08:32:54.398210Z"
- },
- "collapsed": false
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "array([[0.+0.j, 0.-1.j],\n",
- " [0.+1.j, 0.+0.j]])"
- ]
- },
- "execution_count": 24,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "Circuit.from_ir(program_with_unitary).instructions[0].operator.to_matrix()"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "ce9caf39",
- "metadata": {},
- "source": [
- "## Measuring specific qubits with OpenQASM\n",
- "\n",
- "The Local State Vector Simulator and Local Density Matrix Simulator support submitting OpenQASM programs where a subset of the circuit's qubits can be measured, which is often called partial measurement. For example, you can create a two-qubit circuit and only measure the first qubit like so:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "id": "d33a0cb6",
- "metadata": {},
- "outputs": [],
- "source": [
- "partial_measure_qasm = \"\"\"\n",
- "OPENQASM 3.0;\n",
- "bit[1] b;\n",
- "qubit[2] q;\n",
- "h q[0];\n",
- "cnot q[0], q[1];\n",
- "b[0] = measure q[0];\n",
- "\"\"\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "81247da3",
- "metadata": {},
- "source": [
- "We can see that there are two qubits, `q[0]` and `q[1]` but we are only measuring qubit 0 here: `b[0] = measure q[0]`.\n",
- "\n",
- "Now we can run this on the local state vector simulator"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "id": "d747fcb4",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Counter({'0': 7, '1': 3})\n",
- "Measured qubits: [0]\n"
- ]
- }
- ],
- "source": [
- "from braket.devices import LocalSimulator\n",
- "\n",
- "local_sim = LocalSimulator()\n",
- "partial_measure_local_sim_task = local_sim.run(OpenQASMProgram(source=partial_measure_qasm), shots = 10)\n",
- "partial_measure_local_sim_result = partial_measure_local_sim_task.result()\n",
- "print(partial_measure_local_sim_result.measurement_counts)\n",
- "print(\"Measured qubits: \", partial_measure_local_sim_result.measured_qubits)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "923c3cd7",
- "metadata": {},
- "source": [
- "As expected, only the targeted qubits were measured. \n",
- "\n",
- "The OpenQASM program `partial_measure_qasm` is equivalent to the following `Circuit`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "id": "5b4e6ae6",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Counter({'0': 7, '1': 3})\n",
- "Measured qubits: [0]\n"
- ]
- }
- ],
- "source": [
- "partial_measure = Circuit().h(0).cnot(0, 1).measure(0)\n",
- "partial_measure_task = local_sim.run(partial_measure, shots=10)\n",
- "partial_measure_result = partial_measure_task.result()\n",
- "print(partial_measure_result.measurement_counts)\n",
- "print(\"Measured qubits: \", partial_measure_result.measured_qubits)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "49b97636",
- "metadata": {},
- "source": [
- "We 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."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "id": "74a715eb",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "False"
- ]
- },
- "execution_count": 15,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "AwsDevice(Devices.Rigetti.AspenM3).properties.action['braket.ir.openqasm.program'].requiresAllQubitsMeasurement"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a6a94ac1",
- "metadata": {},
- "source": [
- "Here, `requiresAllQubitsMeasurement` is `False`, which indicates that not all qubits must be measured."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "8996e0c59741a66f",
- "metadata": {},
- "source": [
- "## Qubit Rewiring with OpenQASM\n",
- "\n",
- "Amazon Braket supports the [physical qubit notation within OpenQASM](https://openqasm.com/language/types.html#physical-qubits) on Rigetti devices. When using physical qubits, you have to ensure that the qubits are indeed connected on the selected device. Alternatively, if qubit registers are used instead, the `PARTIAL` rewiring strategy is enabled by default on Rigetti devices. The following example shows how to use physical qubit notation in an OpenQASM program:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "id": "144cc3e458a21b2d",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:54.410178Z",
- "start_time": "2023-11-21T08:32:54.402877Z"
- }
- },
- "outputs": [],
- "source": [
- "ghz_program_with_physical_qubits = \"\"\"\n",
- "// Prepare a GHZ state\n",
- "OPENQASM 3;\n",
- "\n",
- "bit[3] ro;\n",
- "h $0;\n",
- "cnot $0, $1;\n",
- "cnot $1, $2;\n",
- "ro[0] = measure $0;\n",
- "ro[1] = measure $1;\n",
- "ro[2] = measure $2;\n",
- "\"\"\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "3f01e6dc44e0e226",
- "metadata": {},
- "source": [
- "We can run the above program on the Rigetti Aspen-M-3 device,"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "id": "768b980b78c4d451",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:55.489511Z",
- "start_time": "2023-11-21T08:32:54.414005Z"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Measured qubits: [0, 1, 2]\n"
- ]
- }
- ],
- "source": [
- "# choose the quantum device\n",
- "aspen_m = AwsDevice(Devices.Rigetti.AspenM3)\n",
- "\n",
- "ghz_program_with_physical_qubits_task = aspen_m.run(OpenQASMProgram(source=ghz_program_with_physical_qubits), shots = 10)\n",
- "measured_qubits = ghz_program_with_physical_qubits_task.result().measured_qubits\n",
- "print(\"Measured qubits:\", measured_qubits)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "5a5597dc7843d541",
- "metadata": {},
- "source": [
- "As we can see, physical qubits 0, 1 and 2 are indeed being used and measured."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "25cb2ccc4337dc1e",
- "metadata": {},
- "source": [
- "
\n",
- " Note: This section and the next verbatim box section uses the Rigetti Aspen-M-3 device. When you run this notebook, make sure the device is currently available. You can find QPU availability windows on the Devices page in the Amazon Braket Console\n",
- "
"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "import networkx as nx\n",
- "# access and visualize the device topology\n",
- "print(aspen_m.properties.paradigm.connectivity.connectivityGraph)\n",
- "nx.draw_kamada_kawai(aspen_m.topology_graph, with_labels=True, font_color=\"white\")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9fa09f83a0747394",
- "metadata": {},
- "source": [
- "Now we can submit a quantum task of the above program with verbatim box."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "id": "b5e7c89c1c9f8879",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:56.299544Z",
- "start_time": "2023-11-21T08:32:56.297194Z"
- },
- "scrolled": true
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\n",
- "DECLARE ro BIT[2]\n",
- "PRAGMA INITIAL_REWIRING \"NAIVE\"\n",
- "RESET\n",
- "RX(pi) 0\n",
- "RX(pi) 0\n",
- "CZ 0 1\n",
- "MEASURE 1 ro[1]\n",
- "MEASURE 0 ro[0]\n"
- ]
- }
- ],
- "source": [
- "verbatim_task = aspen_m.run(OpenQASMProgram(source=program_with_verbatim_box), shots = 10)\n",
- "verbatim_result = verbatim_task.result()\n",
- "meta = verbatim_result.additional_metadata.rigettiMetadata\n",
- "print(meta.compiledProgram)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "bcd27bc239db889d",
- "metadata": {},
- "source": [
- "As shown above, the two consecutive `rx` $\\pi$-rotation gates did not get optimized and we confirm that our program was indeed executed verbatim."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b4768a96f7b1b3b6",
- "metadata": {},
- "source": [
- "## Requesting Result Types with OpenQASM\n",
- "\n",
- "Braket provides [a rich library of result types](https://docs.aws.amazon.com/braket/latest/developerguide/braket-result-types.html) for circuit executions. With OpenQASM, requesting different result types for our quantum tasks is easier than ever using the `result` pragma. Next, we give an example of requesting result types for our Bell state program submitted to SV1. Before doing that, let's see what result types are supported on SV1:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "id": "b17d21060cda7fa7",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:56.304110Z",
- "start_time": "2023-11-21T08:32:56.300283Z"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "name='Sample' observables=['x', 'y', 'z', 'h', 'i', 'hermitian'] minShots=1 maxShots=100000\n",
- "name='Expectation' observables=['x', 'y', 'z', 'h', 'i', 'hermitian'] minShots=0 maxShots=100000\n",
- "name='Variance' observables=['x', 'y', 'z', 'h', 'i', 'hermitian'] minShots=0 maxShots=100000\n",
- "name='Probability' observables=None minShots=1 maxShots=100000\n",
- "name='Amplitude' observables=None minShots=0 maxShots=0\n",
- "name='AdjointGradient' observables=['x', 'y', 'z', 'h', 'i'] minShots=0 maxShots=0\n"
- ]
- }
- ],
- "source": [
- "# print the result types supported by SV1\n",
- "for iter in sv1.properties.action['braket.ir.openqasm.program'].supportedResultTypes:\n",
- " print(iter)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "bbc02495939691c9",
- "metadata": {},
- "source": [
- "With knowing the supported result types on SV1, we choose to request the `Expectation` of $X \\otimes Z$ observable on `q[0]` and `q[1]` and the `Amplitude` result type for a `shots=0` quantum task of our bell program:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "id": "6e3b521b7356b4bd",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:56.305634Z",
- "start_time": "2023-11-21T08:32:56.302835Z"
- }
- },
- "outputs": [],
- "source": [
- "bell_with_result_type = \"\"\"\n",
- "OPENQASM 3;\n",
- "\n",
- "qubit[2] q;\n",
- "\n",
- "#pragma braket result expectation x(q[0]) @ z(q[1])\n",
- "#pragma braket result amplitude \"00\", \"11\"\n",
- "h q[0];\n",
- "cnot q[0], q[1];\n",
- "\"\"\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "6435b34ab2525039",
- "metadata": {},
- "source": [
- "The location of the `result` pragma is very flexible as long as it's after the qubit register definition (if you use physical qubits, you can put `result` pragmas anywhere after the program header).\n",
- "\n",
- "We can submit the above program and receive the results for our requested result types."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "id": "a42b0ebc71b59136",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:59.113501Z",
- "start_time": "2023-11-21T08:32:56.306452Z"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[0.0, {'00': (0.7071067811865475+0j), '11': (0.7071067811865475+0j)}]\n"
- ]
- }
- ],
- "source": [
- "bell_result_types_task = sv1.run(OpenQASMProgram(source=bell_with_result_type), shots = 0)\n",
- "bell_result = bell_result_types_task.result()\n",
- "values = bell_result.values\n",
- "print(values)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "38675f5678613895",
- "metadata": {},
- "source": [
- "At last, we want to remind our Braket OpenQASM users that there are two requirements when requesting result types:\n",
- "1. For `shots=0` quantum tasks, requesting non-simultaneously measurable result types is allowed, but for `shots>0` quantum tasks, it is not allowed. For example, we can write the following OpenQASM program in a `shots=0` quantum task but not in a `shots>0` quantum task, since the two result types are not simultaneously measurable:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "id": "45277f89209faec7",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:59.115339Z",
- "start_time": "2023-11-21T08:32:59.112696Z"
- }
- },
- "outputs": [],
- "source": [
- "program_with_non_simultaneously_measurable_result_types = \"\"\"\n",
- "OPENQASM 3;\n",
- "\n",
- "qubit[2] q;\n",
- "\n",
- "h q[0];\n",
- "cnot q[0], q[1];\n",
- "\n",
- "#pragma braket result expectation x(q[0]) @ z(q[1])\n",
- "#pragma braket result expectation hermitian([[0, -1im], [1im, 0]]) q[0]\n",
- "\"\"\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a9af8819fba31a90",
- "metadata": {},
- "source": [
- "2. Do not use measurement instructions and request result types in the same OpenQASM program, otherwise a validation error will be raised. Since measurement instructions are basically equivalent to `#pragma braket result sample z(qubit)`, we encourage users to adapt a consistent style of requesting result types in the same program."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "db79ad324a5f6478",
- "metadata": {
- "collapsed": false
- },
- "source": [
- "Circuits constructed with `from_ir` will have the correct result types:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "id": "f3c438da3a351153",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:59.127212Z",
- "start_time": "2023-11-21T08:32:59.124909Z"
- },
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "bell_with_result_type:\n",
- "T : |0|1| Result Types |\n",
- " \n",
- "q0 : -H-C-Expectation(X@Z)-\n",
- " | | \n",
- "q1 : ---X-Expectation(X@Z)-\n",
- "\n",
- "T : |0|1| Result Types |\n",
- "\n",
- "Additional result types: Amplitude(00,11)\n"
- ]
- }
- ],
- "source": [
- "print(\"bell_with_result_type:\")\n",
- "print(Circuit.from_ir(bell_with_result_type))"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "c371de3097c0598a",
- "metadata": {
- "collapsed": false
- },
- "source": [
- "## Advanced OpenQASM features\n",
- "\n",
- "OpenQASM has features beyond what is natively supported by the `Circuit` class. You can run OpenQASM tasks directly on the Local Simulator or use Circuit.from_ir to convert an OpenQASM program to a Circuit object, which is supported by all circuit-based Braket devices. The following OpenQASM program is a GHZ state written with classical control flow and subroutines:\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "id": "5acb72003cd0a0f5",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-12-04T04:04:08.355878Z",
- "start_time": "2023-12-04T04:04:08.353679Z"
- },
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "ghz_with_advanced_features = \"\"\"\n",
- "OPENQASM 3.0;\n",
- "\n",
- "def ghz(int[32] n) {\n",
- " h q[0];\n",
- " for int i in [0:n - 1] {\n",
- " cnot q[i], q[i + 1];\n",
- " }\n",
- "}\n",
- "\n",
- "int[32] n = 5;\n",
- "bit[n + 1] c;\n",
- "qubit[n + 1] q;\n",
- "\n",
- "ghz(n);\n",
- "\n",
- "c = measure q;\n",
- "\"\"\"\n",
- "qasm_program = OpenQASMProgram(source=ghz_with_advanced_features)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f5d32bd10c64543",
- "metadata": {
- "collapsed": false
- },
- "source": [
- "The local simulator supports these features:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "id": "c82bee530da348f6",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-12-04T04:04:13.932270Z",
- "start_time": "2023-12-04T04:04:13.863203Z"
- },
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "This program uses OpenQASM language features that may not be supported on QPUs or on-demand simulators.\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "Counter({'000000': 480, '111111': 520})"
- ]
- },
- "execution_count": 36,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "from braket.devices import LocalSimulator\n",
- "LocalSimulator().run(qasm_program, shots=1000).result().measurement_counts"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "96fca192b4f51797",
- "metadata": {
- "collapsed": false
- },
- "source": [
- "but SV1 does not. However, `from_ir` will \"unroll\" the subroutine and loop to create a `Circuit` object that _can_ run on SV1:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "id": "b0886dfc74978bfa",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-12-04T04:04:19.974386Z",
- "start_time": "2023-12-04T04:04:16.705969Z"
- },
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "This program uses OpenQASM language features that may not be supported on QPUs or on-demand simulators.\n"
- ]
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "T : |0|1|2|3|4|5|\n",
- " \n",
- "q0 : -H-C---------\n",
- " | \n",
- "q1 : ---X-C-------\n",
- " | \n",
- "q2 : -----X-C-----\n",
- " | \n",
- "q3 : -------X-C---\n",
- " | \n",
- "q4 : ---------X-C-\n",
- " | \n",
- "q5 : -----------X-\n",
- "\n",
- "T : |0|1|2|3|4|5|\n",
- "Counter({'000000': 503, '111111': 497})\n"
- ]
- }
- ],
- "source": [
- "circuit = Circuit.from_ir(qasm_program)\n",
- "print(circuit)\n",
- "print(sv1.run(circuit, shots=1000).result().measurement_counts)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a329fa3df47ffee4",
- "metadata": {
- "collapsed": false
- },
- "source": [
- "For an in-depth exploration of advanced OpenQASM features, see [Simulating Advanced OpenQASM Programs with the Local Simulator](https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/braket_features/Simulating_Advanced_OpenQASM_Programs_with_the_Local_Simulator.ipynb)."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9e4c7a48a2b83ce1",
- "metadata": {},
- "source": [
- "# Conclusion\n",
- "\n",
- "In this notebook, you learned how to submit OpenQASM quantum tasks and use OpenQASM features on Braket. Hope you enjoyed it! You can find more information about OpenQASM3.0 in its [live specification](https://openqasm.com/), and you can learn more about OpenQASM support on Braket in the [Amazon Braket documentation](https://docs.aws.amazon.com/braket/latest/developerguide/)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "id": "3f5bc3dfad520f14",
- "metadata": {
- "ExecuteTime": {
- "end_time": "2023-11-21T08:32:59.624204Z",
- "start_time": "2023-11-21T08:32:59.127772Z"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Quantum Task Summary\n",
- "{'arn:aws:braket:::device/quantum-simulator/amazon/sv1': {'shots': 120, 'tasks': {'CREATED': 1, 'COMPLETED': 3}, 'execution_duration': datetime.timedelta(microseconds=20000), 'billed_execution_duration': datetime.timedelta(seconds=9)}, 'arn:aws:braket:::device/quantum-simulator/amazon/dm1': {'shots': 30, 'tasks': {'COMPLETED': 3}, 'execution_duration': datetime.timedelta(microseconds=12000), 'billed_execution_duration': datetime.timedelta(seconds=9)}}\n",
- "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).\n",
- "Estimated cost to run this example: 0.02 USD\n"
- ]
- }
- ],
- "source": [
- "print(\"Quantum Task Summary\")\n",
- "print(t.quantum_tasks_statistics())\n",
- "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).')\n",
- "print(f\"Estimated cost to run this example: {t.qpu_tasks_cost() + t.simulator_tasks_cost():.2f} USD\")"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.6"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
+ "nbformat": 4,
+ "nbformat_minor": 5
}
diff --git a/examples/braket_features/Noise_models/Noise_models_on_Rigetti.ipynb b/examples/braket_features/Noise_models/Noise_models_on_Rigetti.ipynb
index 0df61d783..5b00a60a7 100644
--- a/examples/braket_features/Noise_models/Noise_models_on_Rigetti.ipynb
+++ b/examples/braket_features/Noise_models/Noise_models_on_Rigetti.ipynb
@@ -1,1053 +1,997 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "74154c03-d48f-4f41-867b-5e30254ce31a",
- "metadata": {},
- "source": [
- "# Noise models on Rigetti\n",
- "\n",
- "This notebook shows how to construct a noise model from device calibration data for Rigetti Aspen-M-3. We compare the measurement outcomes of circuits run on a noisy simulator with the same circuits run on quantum processing units (QPUs), to show that simulating circuits with noise models more closely mimics QPUs.\n",
- "\n",
- "**Before you begin**: We recommend being familiar with [Noise models on Amazon Braket.](https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/braket_features/Noise_models/Noise_models_on_Amazon_Braket.ipynb)\n",
- "Additionally, users should be familiar with [Running quantum circuits on QPU devices](https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/getting_started/2_Running_quantum_circuits_on_QPU_devices/2_Running_quantum_circuits_on_QPU_devices.ipynb). \n",
- "\n",
- "### Table of Contents\n",
- "\n",
- "- Noise model for Rigetti\n",
- " - Loading device calibration data\n",
- " - Comparing noisy simulator results to QPU results\n",
- " - Smaller noise models compared to QPU results"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "e5e15fcd-55a2-4168-acc6-a38100f94511",
- "metadata": {},
- "outputs": [],
- "source": [
- "import numpy as np\n",
- "import pandas as pd\n",
- "from braket.aws import AwsDevice\n",
- "from braket.circuits import Circuit, Gate\n",
- "from braket.circuits.noise_model import GateCriteria, NoiseModel, ObservableCriteria\n",
- "from braket.circuits.noises import AmplitudeDamping, BitFlip, Depolarizing, PhaseDamping, TwoQubitDepolarizing\n",
- "from braket.devices import Devices, LocalSimulator"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "d4da3e37-e93c-4273-98bc-d67b26b8c822",
- "metadata": {},
- "source": [
- "Braket provides access to hardware providers' reported calibration data. \n",
- "This can be used to construct noise models to approximate the behavior of the QPU when running circuits on a noisy simulator.\n",
- "In this tutorial, we focus on local noise models with no crosstalk interactions. Real devices can have crosstalk and unexpected effects that can further degrade the results.\n",
- "\n",
- "The Aspen-M-3 calibration data is available on the Braket devices page. Under qubit specs, the calibration data include the qubit index, with corresponding values for the $T_1$, $T_2$, fidelity from randomized benchmarking (fRB), fidelity from simultaneous randomized benchmarking (fsRB), readout fidelity (fRO), and active reset fidelity.\n",
- "Under \"edge specs\", the data includes the RB fidelity for C-Phase, XY, and CZ gates for each connected edge in the device topology."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "38001b96-faf7-49f0-b5a5-2d52820fcfc9",
- "metadata": {},
- "source": [
- "**One-qubit calibration data (Qubit specs)**\n",
- "\n",
- "\n",
- "\n",
- "**Two-qubit calibration data (Edge specs)**\n",
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "276d680f-887d-4929-bcaa-e276a6303496",
- "metadata": {},
- "source": [
- "We can programmatically access all the calibration data with the Braket SDK. First we load the AwsDevice using the ARN for Rigetti Aspen-M-3."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "a55ea9d5-05f3-4da5-a305-a391e914d012",
- "metadata": {},
- "outputs": [],
- "source": [
- "aspen_m = AwsDevice(Devices.Rigetti.AspenM3)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "a8a42ae1-51ac-4bf1-bd6a-81ffd982b5f8",
- "metadata": {},
- "source": [
- "The properties dictionary contains keys \"provider\" and \"specs\", with \"1Q\" and \"2Q\" keys for the one- and two-qubit calibration data. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "153b5b63-8205-4a7e-bb18-0305ef428f36",
- "metadata": {},
- "outputs": [],
- "source": [
- "aspen_m_specs = aspen_m.properties.dict()[\"provider\"][\"specs\"]\n",
- "\n",
- "one_qubit_data = aspen_m_specs[\"1Q\"]\n",
- "two_qubit_data = aspen_m_specs[\"2Q\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "661cca17-01fa-40be-bd3e-741bacc74ce4",
- "metadata": {},
- "source": [
- "The keys of the \"1Q\" dictionary is the qubit number. \n",
- "For Aspen-M-3, there are 79 qubits indexed by the first digit representing the octagon (0 to 4, and 10 to 14)\n",
- "and the second representing the qubit in that octagon (0 to 7). We can get all qubit indices with `one_qubit_data.keys()` or with `aspen_m.topology_graph.nodes`.\n",
- "\n",
- "The keys of the \"2Q\" dictionary are the connected qubit pairs separated by a hyphen. \n",
- "For example, if qubit 0 and 1 are connected the key is \"0-1\".\n",
- "\n",
- "\n",
- "#### One-qubit noise \n",
- "\n",
- "Let's look at the one qubit calibration data for qubit 0."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "8d4ecc35-f4dc-49d8-b6ff-adaee560ac4b",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "{'T1': 2.4443429874176914e-05,\n",
- " 'T2': 1.627681336532139e-05,\n",
- " 'f1QRB': 0.9985669301541129,\n",
- " 'f1QRB_std_err': 0.0004092835114091019,\n",
- " 'f1Q_simultaneous_RB': 0.9977676229871197,\n",
- " 'f1Q_simultaneous_RB_std_err': 0.00021522363993495725,\n",
- " 'fActiveReset': 0.9905000000000002,\n",
- " 'fRO': 0.9795}"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "one_qubit_data[\"0\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "4b676762-e642-4a98-89e3-86d79367071a",
- "metadata": {},
- "source": [
- "For each qubit, there are various metrics of the quality:\n",
- "\n",
- "- **T1**: Thermal relaxation time is related to the time it takes for the excited state, |1⟩, to decay into the ground state, |0⟩. The probability of remaining in the excited state is $p(|1⟩)\\sim e^{-t/T_1}$\n",
- "\n",
- "- **T2**: The dephasing time, is the decay constant for the scale for a |+⟩ state to decohere into the completely mixed state. $p(|+⟩)\\sim e^{-t/T_2}$ \n",
- "\n",
- "- **Fidelity (RB)**: Single-qubit randomized benchmarking fidelities. RB fidelity quantifies the average gate fidelity where the average is over all Clifford gates. RB describes an *effective* noise model with gate-independent depolarizing noise on each Clifford gate.\n",
- "\n",
- "- **Fidelity (sRB)**: Single-qubit simultaneous randomized benchmarking fidelities. These are extracted by running single-qubit RB on all qubits simultaneously. Note that we expect the sRB fidelity to be lower than standard RB fidelity due to non-local crosstalk type noise on the device. \n",
- "\n",
- "- **Active reset fidelity**: Single-qubit active reset fidelities represents the accuracy to which qubits are reinitalized into the ground state |0⟩.\n",
- "\n",
- "- **Readout fidelity**: Single-qubit readout fidelities describes the probability of a bit flip error before readout in the computational basis. The readout fidelity is related to the probability of correctly measuring the ground state and excited states respectively, e.g. $f_{RO} =\\frac{p(0|0)+p(1|1)}{2}$"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "296708a3-771e-42b4-9ba8-f3db1a02970b",
- "metadata": {},
- "source": [
- "Now that we know how to extract and use the calibration data, we can build a simple noise model. For every qubit we will add:\n",
- "- amplitude dampening noise with probability $p= 1-e^{-t/T_1}$ for every gate\n",
- "- phase dampening noise with probability $p= 0.5(1-e^{-t/T_2})$ for every gate\n",
- "- depolarizing noise with probability $p=1-f_{sRB}$ (from simultaneous RB fidelity) for every gate\n",
- "- readout bit flip noise with probability $p=1-f_{RO}$ to measurements \n",
- "\n",
- "Technically, the sRB fidelity already includes effects from $T_1$/$T_2$, however to be explicit we add these as separate terms. In a sense, this model might overestimate the noise on the QPU. \n",
- "\n",
- "For the dampening noises, we first need the gate times to model $T_1$ and $T_2$ errors.\n",
- "From the Braket Aspen-M-3 device page: \"These gates offer fast (40ns and 180ns) 1Q and 2Q gate times.\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "cd27adfb-f7d6-4583-b854-794e38152c90",
- "metadata": {},
- "outputs": [],
- "source": [
- "# median 1q and 2q gate times\n",
- "gate_time_1_qubit = 40e-9\n",
- "gate_time_2_qubit = 240e-09"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "5769f2f4-de1c-4b47-9e4e-fd8d39b9e975",
- "metadata": {},
- "source": [
- "To create the noise model, we iterate over all qubits keys in `one_qubit_data`"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "508070bc-b1d2-4737-a2fc-d7711e419996",
- "metadata": {},
- "outputs": [],
- "source": [
- "noise_model = NoiseModel()\n",
- "\n",
- "# Single-qubit noise\n",
- "for q, data in one_qubit_data.items(): # iterate over qubits\n",
- "\n",
- " # T1 dampening\n",
- " t1 = data[\"T1\"]\n",
- " damping_prob = 1 - np.exp(-(gate_time_1_qubit / t1))\n",
- " noise_model.add_noise(AmplitudeDamping(damping_prob), GateCriteria(qubits=int(q)))\n",
- "\n",
- " # T2 phase flip\n",
- " t2 = data[\"T2\"]\n",
- " dephasing_prob = 0.5 * (1 - np.exp(-(gate_time_1_qubit / t2)))\n",
- " noise_model.add_noise(PhaseDamping(dephasing_prob), GateCriteria(qubits=int(q)))\n",
- "\n",
- " # 1q RB depolarizing rate from simultaneous RB\n",
- " depolar_rate = 1 - data[\"f1Q_simultaneous_RB\"]\n",
- " noise_model.add_noise(Depolarizing(depolar_rate), GateCriteria(qubits=int(q)))\n",
- "\n",
- " # 1q RB readout\n",
- " readout_value = 1 - data[\"fRO\"]\n",
- " noise_model.add_noise(BitFlip(readout_value), ObservableCriteria(qubits=int(q)))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "9d20d24c-17e2-4bba-b317-fb2d156bedb0",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Number of terms in noise model is: 320\n",
- "Number of parameters in noise model is: 320\n"
- ]
- }
- ],
- "source": [
- "num_params = sum(len(item.noise.parameters) for item in noise_model.instructions)\n",
- "print(f\"Number of terms in noise model is: {len(noise_model.instructions)}\")\n",
- "print(f\"Number of parameters in noise model is: {num_params}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "790c62f7-776a-4e4c-9405-7b7a14eee5b0",
- "metadata": {},
- "source": [
- "#### Two-qubit noise \n",
- "Next we consider adding two-qubit noise to the model. \n",
- "\n",
- "Let's first look at the data provided in the Aspen-M-3 device calibration data. On the first connect, \"0-1\", the properties are:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "id": "626cbe56-3649-48eb-a2a1-f669eb790b47",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "{'fCPHASE': 0.8991973184700036,\n",
- " 'fCPHASE_std_err': 0.019391126441846068,\n",
- " 'fCZ': 0.9406104552003762,\n",
- " 'fCZ_std_err': 0.011069113247524152,\n",
- " 'fXY': 0.8365560014004874,\n",
- " 'fXY_std_err': 0.014585224789608713}"
- ]
- },
- "execution_count": 8,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "two_qubit_data[\"0-1\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "4bcd3be4-a6e4-4cf3-841e-1a451e01803a",
- "metadata": {},
- "source": [
- "Here, we see the fidelity per gate (CPhase, CZ, or XY) and the associated standard error. \n",
- "\n",
- "Next we loop over the entries in the `two_qubit_data` dictionary and add two-qubit depolarizing noise to the model. Notice that Aspen-M-3 has symmetric connections (\"0-1\" and \"1-0\") so we need to add noise in both directions."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "id": "80f57550-6549-46b6-9a6d-ff7f0f03e90d",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Two-qubit noise\n",
- "for pair, data in two_qubit_data.items(): # iterate over qubit connections\n",
- "\n",
- " # parse strings \"0-1\" to integers [0, 1]\n",
- " q0, q1 = (int(s) for s in pair.split(\"-\"))\n",
- "\n",
- " if \"fCPHASE\" in data:\n",
- " phase_rate = 1 - data[\"fCPHASE\"]\n",
- " noise_model.add_noise(\n",
- " TwoQubitDepolarizing(phase_rate),\n",
- " GateCriteria(\n",
- " Gate.CPhaseShift, [(q0, q1), (q1, q0)]\n",
- " ), # symmetric connections\n",
- " )\n",
- "\n",
- " if \"fXY\" in data:\n",
- " xy_rate = 1 - data[\"fXY\"]\n",
- " noise_model.add_noise(\n",
- " TwoQubitDepolarizing(xy_rate), GateCriteria(Gate.XY, [(q0, q1), (q1, q0)])\n",
- " )\n",
- "\n",
- " if \"fCZ\" in data:\n",
- " cz_rate = 1 - data[\"fCZ\"]\n",
- " noise_model.add_noise(\n",
- " TwoQubitDepolarizing(cz_rate), GateCriteria(Gate.CZ, [(q0, q1), (q1, q0)])\n",
- " )"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "id": "82376d08-84bc-4249-9c8d-d7aacdb73113",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Number of terms in noise model is: 612\n",
- "Number of parameters in noise model is: 612\n"
- ]
- }
- ],
- "source": [
- "num_params = sum(len(item.noise.parameters) for item in noise_model.instructions)\n",
- "print(f\"Number of terms in noise model is: {len(noise_model.instructions)}\")\n",
- "print(f\"Number of parameters in noise model is: {num_params}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "5913a8a2-a9de-4e32-a35d-c48191891b1f",
- "metadata": {},
- "source": [
- "### Compare circuits run on device vs simulator with a noise model\n",
- "\n",
- "Let's just look at the first 5 qubits. Note that to ensure the noise model applied T1 and T2 noise during the time between gate, we manually add identity gates to each moment."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "id": "6fabbd05-b480-43c6-b43a-d396d81bd687",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "T : | 0 | 1 |2|\n",
- " \n",
- "q0 : -Rx(0.50)-Rx(3.14)-C-\n",
- " | \n",
- "q1 : -Rz(0.50)-Rx(3.14)-Z-\n",
- " \n",
- "q2 : -Rz(0.50)-Rx(3.14)---\n",
- "\n",
- "T : | 0 | 1 |2|\n"
- ]
- }
- ],
- "source": [
- "np.random.seed(42)\n",
- "\n",
- "circ = Circuit().rx(0, 0.5).rz(1, 0.5).rz(2, 0.5).rx(0, np.pi).rx(1, np.pi).rx(2, np.pi).cz(0, 1)\n",
- "print(circ)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "id": "e2af07ed-33de-4709-9e7c-5e3c3caaa751",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "T : | 0 | 1 | 2 |\n",
- " \n",
- "q0 : -Rx(0.50)-AD(0.0016)--PD(0.0012)-DEPO(0.0022)-Rx(3.14)-AD(0.0016)--PD(0.0012)-DEPO(0.0022)-C-DEPO(0.059)-\n",
- " | | \n",
- "q1 : -Rz(0.50)-AD(0.00086)-PD(0.0015)-DEPO(0.025)--Rx(3.14)-AD(0.00086)-PD(0.0015)-DEPO(0.025)--Z-DEPO(0.059)-\n",
- " \n",
- "q2 : -Rz(0.50)-AD(0.0018)--PD(0.0011)-DEPO(0.0041)-Rx(3.14)-AD(0.0018)--PD(0.0011)-DEPO(0.0041)---------------\n",
- "\n",
- "T : | 0 | 1 | 2 |\n"
- ]
- }
- ],
- "source": [
- "noisy_circ = noise_model.apply(circ)\n",
- "\n",
- "print(noisy_circ)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "id": "9304dcf1-a1e8-4e41-a6b5-769841f4eefe",
- "metadata": {},
- "outputs": [],
- "source": [
- "simulator = LocalSimulator() # noise free simulator\n",
- "task = simulator.run(circ, shots=100_000)\n",
- "free_probs = task.result().measurement_probabilities"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "id": "0e1d9cfe-03d6-406b-b4ab-89166a907681",
- "metadata": {},
- "outputs": [],
- "source": [
- "noisy_simulator = LocalSimulator(\"braket_dm\")\n",
- "noisy_task = noisy_simulator.run(noisy_circ, shots=100_000)\n",
- "noisy_probs = noisy_task.result().measurement_probabilities"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b3f370bd-a8b3-4d5b-a724-da549fc4c81f",
- "metadata": {},
- "source": [
- "
\n",
- "Note: The below section runs tasks on the Rigetti Aspen-M-3 device. When you run this notebook, make sure the device is currently available. You can find QPU availability windows on the Devices page in the Amazon Braket Console.\n",
- "
\n",
- "\n",
- "
\n",
- "Note: Running the circuit below will result in charges on your AWS account.\n",
- "
"
- ]
- },
- "metadata": {
- "needs_background": "light"
- },
- "output_type": "display_data"
- }
- ],
- "source": [
- "import matplotlib.pyplot as plt\n",
- "\n",
- "%matplotlib inline\n",
- "\n",
- "df.plot.bar(\n",
- " title=\"Comparing noise-free simulator, noisy simulator, and Aspen-M\",\n",
- " figsize=(12, 6),\n",
- ")\n",
- "\n",
- "text = f\"f_free_aspen = {f_free_aspen:.3f} \\nf_noise_model_aspen = {f_noisy_aspen:.3f}\"\n",
- "plt.text(1, 0.5, text, fontsize=14)\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "aa8ffab8-e062-4c33-9c9d-4a312cd76b63",
- "metadata": {},
- "source": [
- "We confirm that the simulator with a noise model is closer to the distribution produced by Aspen-M-3."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "c4571a5e-2f8d-4767-b28e-a527d2490830",
- "metadata": {},
- "source": [
- "### Smaller, reduced noise models\n",
- "\n",
- "The full Rigetti Aspen-M-3 noise model contains due to non-uniform qubit noise. We can obtain simpler, smaller noise models by coarse graining the model above.\n",
- "\n",
- "Here, we consider taking the average over all qubits for the $T_1$, $T_2$, depolarizing, and readout depolarizing rates. This is a substantially smaller noise model, but may be less accurate. \n",
- "\n",
- "We use the `from_filter` function to extract all instructions with amplitude dampening noise in the model. We then compute the mean of the error probabilities. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "id": "eac8479f-4b29-42d8-a15a-ba10aae82d3f",
- "metadata": {},
- "outputs": [],
- "source": [
- "avg_t1 = np.mean(\n",
- " [\n",
- " n.noise.parameters\n",
- " for n in noise_model.from_filter(noise=AmplitudeDamping).instructions\n",
- " ]\n",
- ")\n",
- "avg_t2 = np.mean(\n",
- " [\n",
- " n.noise.parameters\n",
- " for n in noise_model.from_filter(noise=PhaseDamping).instructions\n",
- " ]\n",
- ")\n",
- "avg_depo = np.mean(\n",
- " [\n",
- " n.noise.parameters\n",
- " for n in noise_model.from_filter(noise=Depolarizing).instructions\n",
- " ]\n",
- ")\n",
- "avg_readout = np.mean(\n",
- " [n.noise.parameters for n in noise_model.from_filter(noise=BitFlip).instructions]\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "d1a64cae-226c-45d2-9af3-4b5b7a36c7a4",
- "metadata": {},
- "source": [
- "Now we construct a new noise model with the mean values above:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "id": "e49eb24c-5f24-4084-9d4c-c2e7e82fc259",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Gate Noise:\n",
- " AmplitudeDamping(0.0019483910859341453), GateCriteria(None, None)\n",
- " PhaseDamping(0.0018677309884621795), GateCriteria(None, None)\n",
- " Depolarizing(0.007153677053977023), GateCriteria(None, None)\n",
- "Readout Noise:\n",
- " BitFlip(0.053481250000000015), ObservableCriteria(None, None)\n"
- ]
- }
- ],
- "source": [
- "simple_noise_model = NoiseModel()\n",
- "simple_noise_model.add_noise(AmplitudeDamping(avg_t1), GateCriteria())\n",
- "simple_noise_model.add_noise(PhaseDamping(avg_t2), GateCriteria())\n",
- "simple_noise_model.add_noise(Depolarizing(avg_depo), GateCriteria())\n",
- "simple_noise_model.add_noise(BitFlip(avg_readout), ObservableCriteria())\n",
- "\n",
- "print(simple_noise_model)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b44bbbd1-fb65-4b8f-8dfa-0ea5aa7979b2",
- "metadata": {},
- "source": [
- "We can see the resultant circuits contain qubit-independent noise:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "id": "c83e96f6-54b0-445b-bcdd-8a7a16691b62",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "T : | 0 | 1 | 2 |\n",
- " \n",
- "q0 : -Rx(0.75)-AD(0.0019)-PD(0.0019)-DEPO(0.0072)-C-AD(0.0019)-PD(0.0019)-DEPO(0.0072)-I--------AD(0.0019)-PD(0.0019)-DEPO(0.0072)-\n",
- " | \n",
- "q1 : -I--------AD(0.0019)-PD(0.0019)-DEPO(0.0072)-Z-AD(0.0019)-PD(0.0019)-DEPO(0.0072)-Rx(0.50)-AD(0.0019)-PD(0.0019)-DEPO(0.0072)-\n",
- " \n",
- "q2 : -Rz(0.50)-AD(0.0019)-PD(0.0019)-DEPO(0.0072)-I-AD(0.0019)-PD(0.0019)-DEPO(0.0072)-I--------AD(0.0019)-PD(0.0019)-DEPO(0.0072)-\n",
- "\n",
- "T : | 0 | 1 | 2 |\n"
- ]
- }
- ],
- "source": [
- "simple_noisy_circ = simple_noise_model.apply(circ)\n",
- "print(simple_noisy_circ)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "e2c20b79-cbf2-4897-80b3-452c78d56a03",
- "metadata": {},
- "source": [
- "We run the circuit on a noisy simulator below"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "id": "a1bb19e8-6824-4a50-b9fb-ae06c14359b6",
- "metadata": {},
- "outputs": [],
- "source": [
- "simple_noisy_task = noisy_simulator.run(simple_noisy_circ, shots=100_000)\n",
- "simple_noisy_probs = simple_noisy_task.result().measurement_probabilities"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "76418e5c-be5b-4b3a-8916-c0f56f003cd7",
- "metadata": {},
- "source": [
- "and add it to the previous dataframe"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "id": "b4d27a95-612d-4dea-9f6a-98d4525b0a70",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- "
\n",
- "
\n",
- "
aspen-m
\n",
- "
noisy_sim
\n",
- "
free_sim
\n",
- "
simple_noisy_sim
\n",
- "
\n",
- " \n",
- " \n",
- "
\n",
- "
000
\n",
- "
0.53302
\n",
- "
0.74544
\n",
- "
0.81380
\n",
- "
0.78149
\n",
- "
\n",
- "
\n",
- "
100
\n",
- "
0.11748
\n",
- "
0.13151
\n",
- "
0.12486
\n",
- "
0.13176
\n",
- "
\n",
- "
\n",
- "
010
\n",
- "
0.24681
\n",
- "
0.08706
\n",
- "
0.05338
\n",
- "
0.06130
\n",
- "
\n",
- "
\n",
- "
110
\n",
- "
0.05687
\n",
- "
0.02742
\n",
- "
0.00796
\n",
- "
0.01061
\n",
- "
\n",
- "
\n",
- "
011
\n",
- "
0.01819
\n",
- "
0.00055
\n",
- "
NaN
\n",
- "
0.00096
\n",
- "
\n",
- "
\n",
- "
111
\n",
- "
0.00477
\n",
- "
0.00021
\n",
- "
NaN
\n",
- "
0.00015
\n",
- "
\n",
- "
\n",
- "
001
\n",
- "
0.01892
\n",
- "
0.00650
\n",
- "
NaN
\n",
- "
0.01179
\n",
- "
\n",
- "
\n",
- "
101
\n",
- "
0.00394
\n",
- "
0.00131
\n",
- "
NaN
\n",
- "
0.00194
\n",
- "
\n",
- " \n",
- "
\n",
- "
"
- ],
- "text/plain": [
- " aspen-m noisy_sim free_sim simple_noisy_sim\n",
- "000 0.53302 0.74544 0.81380 0.78149\n",
- "100 0.11748 0.13151 0.12486 0.13176\n",
- "010 0.24681 0.08706 0.05338 0.06130\n",
- "110 0.05687 0.02742 0.00796 0.01061\n",
- "011 0.01819 0.00055 NaN 0.00096\n",
- "111 0.00477 0.00021 NaN 0.00015\n",
- "001 0.01892 0.00650 NaN 0.01179\n",
- "101 0.00394 0.00131 NaN 0.00194"
- ]
- },
- "execution_count": 22,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "simple_noisy_sim = pd.DataFrame.from_dict(simple_noisy_probs, orient=\"index\").rename(\n",
- " columns={0: \"simple_noisy_sim\"}\n",
- ")\n",
- "df = df.join(simple_noisy_sim)\n",
- "df"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b1699bc2-712f-4a99-b9d6-0a0920114a13",
- "metadata": {},
- "source": [
- "We compute the fidelity between the simple noise model and the QPU:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "id": "d7710e0b-29ac-4968-aa00-e23cb8b32269",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\n",
- "Total fidelity between Aspen-M and full noise model is: 0.9582415634979304\n",
- "\n",
- "Total fidelity between Aspen-M and simple noise model is: 0.9401126418474742\n",
- "\n",
- "Total fidelity between Aspen-M and noise-free is: 0.915784824249657\n"
- ]
- }
- ],
- "source": [
- "f_simple = fidelity(df[\"simple_noisy_sim\"], df[\"aspen-m\"])\n",
- "\n",
- "print(f\"\\nTotal fidelity between Aspen-M and full noise model is: {f_noisy_aspen}\")\n",
- "print(f\"\\nTotal fidelity between Aspen-M and simple noise model is: {f_simple}\")\n",
- "print(f\"\\nTotal fidelity between Aspen-M and noise-free is: {f_free_aspen}\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "id": "e36e19d0-664a-4cb4-a2e3-0061cd60a6c1",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "image/png": "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ERERyQR58+b1/Z4rVy4OHDhwzmVCQ0NZuXIlt9xyCx9//DHt2rUDoEWLFiQkJLBkyRJOnz5NnTp10lzHggULGDBgAD/88AONGzfm1KlTadYWEhKSrM6QkJBU2+cUCsIiIiIiWSA8PJwiRYqwbNkyAKZPn+7rHT7jr7/+4uDBg9xwww288sorrF271jevd+/e9OjRI93e4MTERHbs2ME111zD888/z8GDB/nrr78yZoeyId01QkRERIJPgG93dqGmTZtGv379OHr0KFWqVPENdzjj8OHDdO7cmWPHjuGc4+WXX/bN69mzJyNGjKB79+5prv/06dPccccdHDx4EOccDzzwAIULF86o3cl2LKueexEdHe1iY2OzZNvnI2palF/tZj3n/9cGkT/GX2g5IiIicgHi4+OJjMxZd26aPXs2c+fOZfr06VldyiUltb+1ma12zkWnbKseYREREZFsZtCgQXzyyScsXLjw3I0lTQrCIiIiItnMhAkTzpo2YMAAli9fnmzagw8+mO4Y4mCnICwiIiKSA0ycODGrS8h2dNcIEREREQlKCsIiIiIiEpQUhEVEREQkKGmMsIiIiAQdf2+P6q/1fdYHdH2SOYIzCD8V7n/biIoZV4eIiIgEjfHjx/PGG2/QsGFDZsyYkSnbbN68Od9++22mbCs7Cs4gLCIiIpLJXn/9dT7//HPKly/vm3bq1ClCQzMujikEp09jhEVEREQyWL9+/di2bRvt27cnPDycXr160aJFC3r16sXevXu5+eabady4MY0bN/bdC/jIkSPcddddNGnShAYNGjB37tw0179x40aaNGlC/fr1qVu3Lps3bwagYMGCACxZsoSWLVvSuXNnqlSpwvDhw5kxYwZNmjQhKiqKrVu3ZvxBuASpR1hEREQkg02aNIlPP/2Ur776itdee4358+fzzTffkC9fPnr06MGQIUO48sor2b59O23btiU+Pp7Ro0dz7bXXMnnyZA4cOECTJk1o3bo1BQoUSHX9Dz74ID179uTEiROcPn36rDZr164lPj6eokWLUqVKFfr27cvKlSt59dVXmTBhAuPGjcuEI3FpURAWERERyWSdOnUiX758AHz++eds2rTJN+/QoUP89ddfLF68mHnz5vHSSy8BcOzYMbZv305kZORZ62vWrBmjR49m586ddO3alWrVqp3VpnHjxpQpUwaAyy+/nDZt2gAQFRXFV199FfB9zA4UhEVEREQyWdJe3cTERL777jvCwsKStXHOMWfOHGrUqHHO9fXo0YMrrriCBQsWcMMNN/Dmm29y7bXXJmuTN29e3+8hISG+1yEhIZw6depidifbUhAWERGRoHMp3e6sTZs2TJgwgaFDhwIQFxdH/fr1adu2LRMmTGDChAmYGWvWrKFBgwaprmPbtm1UqVKFBx54gO3bt7Nu3bqzgrCcTRfLiYiIiGSh8ePHExsbS926dalVqxaTJk0C4IknnuDkyZPUrVuX2rVr88QTT6S5jlmzZlGnTh3q16/Phg0b6N27d2aVn62Zcy5LNhwdHe1iY2OzZNvncx/hKD/vIzzrOf+/Uoj8Md7vtiIiInLx4uPjUx1bKzlPan9rM1vtnItO2VY9wiIiIiISlDRGWERERCSbWLRoEcOGDUs2LSIigg8//DCLKsreFIRFREREsom2bdvStm3brC4jx9DQCBEREREJSgrCIiIiIhKU/ArCZtbOzH4ysy1mNjyV+a+YWZz3539mdiDglYqIiIiIBNA5xwibWS5gInA9sBNYZWbznHO+ZwE654YkaT8ISP1uzyIiIiKXgPiagb2Vmm6Nmj350yPcBNjinNvmnDsBvA90Tqd9d+C9QBQnIiIiklP17duXTZs2nbuhHwoWLBiQ9ZzLyJEj+fzzzzNs/fPmzWPMmDEZtv6U/LlrRDlgR5LXO4ErUmtoZpWACODLNObfC9wLULGifw+qEBEREcmJ3n777awu4byNGjUqQ9ffqVMnOnXqlKHbSCrQF8t1A2Y7506nNtM595ZzLto5F12iRIkAb1pERETk0nTkyBE6dOhAvXr1qFOnDjNnzqRVq1acecpuwYIFGTp0KLVr16Z169asXLmSVq1aUaVKFebNmwfA1KlT6dy5M61ataJatWo8/fTTqW7rxRdfpHHjxtStW5cnn3wyzZoSEhKIjIzknnvuoXbt2rRp04a///4bgLi4OJo2bUrdunXp0qULf/75JwAxMTHMnj0bgOHDh1OrVi3q1q3Lww8/zOHDh4mIiODkyZMAHDp0KNnrlMaPH+9bvlu3br59HDhwoG9b/fv3p2nTplSpUoUlS5Zw1113ERkZSUxMzPkc/jT5E4R3ARWSvC7vnZaabmhYhIiIiEgyn376KWXLlmXt2rVs2LCBdu3aJZt/5MgRrr32WjZu3EihQoUYMWIEn332GR9++CEjR470tVu5ciVz5sxh3bp1/Pe///UF6TMWL17M5s2bWblyJXFxcaxevZqlS5emWdfmzZsZMGAAGzdupHDhwsyZMweA3r178/zzz7Nu3TqioqLOCt379+/nww8/ZOPGjaxbt44RI0ZQqFAhWrVqxYIFCwB4//336dq1K7lz505122PGjGHNmjWsW7eOSZMmpdrmzz//ZMWKFbzyyit06tSJIUOGsHHjRtavX09cXFya++Uvf4LwKqCamUWYWR48YXdeykZmVhMoAqy46KpEREREcpCoqCg+++wzhg0bxrJlywgPD082P0+ePL5wHBUVRcuWLcmdOzdRUVEkJCT42l1//fUUK1aMfPny0bVrV7755ptk61m8eDGLFy+mQYMGNGzYkB9//JHNmzenWVdERAT169cHoFGjRiQkJHDw4EEOHDhAy5YtAejTp89ZYTo8PJywsDDuvvtuPvjgA/Lnzw94xj1PmTIFgClTpnDnnXemue26devSs2dP3n33XUJDUx+t27FjR8yMqKgoSpUqRVRUFCEhIdSuXTvZcblQ5wzCzrlTwEBgERAPzHLObTSzUWaWdBBHN+B955y76KpEREREcpDq1avzww8/EBUVxYgRI84aa5s7d27MDICQkBDy5s3r+/3UqVO+dmfapPXaOcejjz5KXFwccXFxbNmyhbvvvjvNus5sByBXrlzJtpWe0NBQVq5cyS233MLHH3/sC/EtWrQgISGBJUuWcPr0aerUqZPmOhYsWMCAAQP44YcfaNy4carbTnocktaa8rhcKL8eseycWwgsTDFtZIrXT110NSIiIiKZILNvd/brr79StGhR7rjjDgoXLnzBF8p99tln/PHHH+TLl4+PPvqIyZMnJ5vftm1bnnjiCXr27EnBggXZtWsXuXPnpmTJkn5vIzw8nCJFirBs2TKuuuoqpk+f7usdPuOvv/7i6NGj3HDDDbRo0YIqVar45vXu3ZsePXrwxBNPpLmNxMREduzYwTXXXMOVV17J+++/z19//eV3jYHiVxAWERERkQu3fv16hg4dSkhICLlz5+aNN97g4YcfPu/1NGnShJtvvpmdO3dyxx13EB0dnWx+mzZtiI+Pp1mzZoDnIrx33333vIIwwLRp0+jXrx9Hjx6lSpUqvuEOZxw+fJjOnTtz7NgxnHO8/PLLvnk9e/ZkxIgRdO/ePc31nz59mjvuuIODBw/inOOBBx6gcOHC51VjIFhWjWSIjo52KQd4Z5qnws/dxisqwr/bvM16zv/ued10W0REJHPFx8cTGRnYh2hktqlTpxIbG8trr72W1aWka/bs2cydO5fp06dnyfZT+1ub2WrnXHTKtuoRFhEREZGAGDRoEJ988gkLFy48d+NLgIKwiIiISDYQExNzQffP3b9/P9ddd91Z07/44guKFSsWgMr+34QJE86aNmDAAJYvX55s2oMPPpjuHSUyi4KwiIiIBAXn3Fl3WQgGxYoVC8g9dy/UxIkTM21b5zvkN9BPlhMRERG55ISFhbF///7zDkqSfTjn2L9/P2FhYX4vox5hERERyfHKly/Pzp072bt3b1aXIhkoLCyM8uXL+91eQVhERERyvNy5cxMREZHVZcglRkMjRERERCQoKQiLiIiISFBSEBYRERGRoKQgLCIiIiJBSUFYRERERIKSgrCIiIiIBCUFYREREREJSgrCIiIiIhKUFIRFREREJCgpCIuIiIhIUFIQFhEREZGgpCAsIiIiIkFJQVhEREREgpKCsIiIiIgEJQVhEREREQlKCsIiIiIiEpQUhEVEREQkKCkIi4iIiEhQUhAWERERkaCkICwiIiIiQUlBWDJdYmIi9913H8WKFcPMWLJkSUDbi4iIiPjDryBsZu3M7Ccz22Jmw9Noc5uZbTKzjWb2n8CWKTnJwoULmTJlCvPnz2f37t00b948oO0vRdu3b6djx44UKFCA4sWL88ADD3DixIl0l9m6dStdunShRIkSXHbZZdx222389ttvydqMHj2aFi1aUKBAAcwszXW9++671K9fn7CwMIoXL07v3r0Dsl8iIiLZ2TmDsJnlAiYC7YFaQHczq5WiTTXgUaCFc642MDjwpUpOsWXLFsqUKUPz5s0pXbo0efLkuej25wqVWen06dN06NCBw4cPs2zZMt577z1mz57NQw89lOYyR44coU2bNjjn+PLLL1m+fDknTpygY8eOJCYm+todP36crl27Mnjw4DTXNX78eIYOHcrDDz/Mhg0b+Oqrr+jcuXMgd1FERCRbCvWjTRNgi3NuG4CZvQ90BjYlaXMPMNE59yeAc+73QBcqOUNMTAzTpk0DwMyoVKkSCQkJ592+VatWREZGUqBAAaZNm0blypVZtWoVmzZtYujQ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zCyAFWYfRYsWpVChQlldRlD4+eefueGGG2jevDlr1qzh0UcfZdCgQcyZMyfNZdauXcsNN9zA9ddfz5o1a5g5cybz5s1j+PDhZ7U9ceIE3bp14+qrrw7ItkVE5NwUhCVTxcTEMGTIELZv346ZUbly5XMus3TpUpo2bUrBggUJDw+nSZMmbNiwATh7aMTUqVMpWLAgn3zyCTVr1iR//vx06tSJgwcPMnv2bKpVq0Z4eDi9evXi77//9m2jVatW9OvXjwcffJAiRYpQpEgRhg4dSmJiYpp1nThxgmHDhlG+fHny589P48aNWbRokV/H4UzdX3zxBVdccQX58+cnOjqaH374IVm7Dz74gKioKPLmzUuFChUYPXo0Se/9nXJoxAcffEDdunXJly8fRYsWpWXLlvz222+++fPnz/f1KkZERPD4449z4sQJv2quXLkyo0aNIiYmhkKFClGhQgVmzpzJgQMH6NatGwULFqRatWosXrw42XJLly7liiuuICwsjFKlSjFkyJBk2zx69CgxMTEULFiQUqVK8eyzz5617Ys51oEyadIkypYty4QJE4iMjOSee+6hT58+Zz3kJKmZM2dSq1Ytnn76aapWrUrLli154YUXmDhxIocPH07WdtiwYdStW5dbb701INsWEZFzUxCWTPXqq68ycuRIypcvz+7du1m1alW67U+dOkXnzp258sorWbt2Ld9//z2DBw8mV65caS5z/Phxxo4dy4wZM/jiiy+IjY3l5ptvZtq0acyZM4ePPvqIjz/+mNdffz3ZcjNmzCAxMZEVK1bw5ptv8tZbbzFu3Lg0t3PnnXfy9ddf85///IcNGzbQp08fOnbsyNq1a/0+Ho8++ihjxozhhx9+oFixYvTs2dMXdFevXs2tt95K165dWb9+PWPGjOG5557jtddeS3Vde/bsoVu3bvTp04f4+HiWLl1Kr169fPMXLVpEz549GThwIBs3bmTy5MnMnj2bxx57zO96x40bR5MmTfjhhx+47bbb6NOnDz169OCGG24gLi6Oq6++mjvuuINjx44BsGvXLtq3b0+DBg1Ys2YN//73v3nvvfeSPc764Ycf5rPPPmPOnDl88cUXrFmzhqVLlwb8WD/77LPp3qqvYMGCLFu2LM3lV6xYQZs2bZJNa9u2LbGxsZw8eTLVZY4fP05YWFiyafny5ePYsWOsXr3aN23BggV8/PHHTJgwIWDbFhERPzjnsuSnUaNGLss8eZnfP3Wm1vHrZ1ONmn7/BLsXX3zRVapUya+2+/fvd4BbsmRJqvO/+uorB7i9e/c655ybMmWKA9yPP/7oa/PQQw+5kJAQXxvnnOvTp4/r0KGD73XLli1dtWrVXGJiom/aM88848qVK5eszYABA5xzzm3ZssWZmfvll1+S1dO5c2fXv3//c+7Xmbo//fRT37RvvvnGAW7Hjh3OOed69OjhrrnmmmTLPfnkk2nWtHr1age4hISEVLd51VVXuVGjRiWb9uGHH7oCBQok2++0VKpUyXXr1s33+vDhww5wgwYN8k37+eefHeBWrVrlnHPusccec1WrVnWnT5/2tZkyZYrLkyePO3LkiDt8+LDLkyePe/fdd5OtNzw83PXp08c559+xTrnd1Ozfv99t3rw53Z+jR4+muXy1atXc008/nWza119/7QD366+/prrMokWLnJm56dOnu5MnT7qdO3e6q666ygHuP//5j3POuV27drkyZcq47777znd8ChQocNHbFhGR/wfEulTyqHqE5ZJWtGhRYmJiaNu2LR06dODll18+50M28ubNS40aNXyvS5UqRenSpSlevHiyab///nuy5Zo2bZpsrHGzZs3YtWsXhw4dOmsbP/zwA845atWqlaxHccGCBWzdutXv/atbt67v9zP3TD5TV3x8PC1atEjW/sorr0yzpnr16tG6dWvq1KnDzTffzBtvvMHevXt981evXs3o0aOT1dujRw+OHDnCnj17zrveggULkj9/fqKionzTSpUqddY+NG3alJCQ//+v5sorr+TEiRNs2bKFrVu3cuLECZo1a5ZsvUnXGahjXbRoUapWrZruT758+fxenz/atGnDSy+9xIABAwgLC6N69erccMMNAL5j0qtXL/r3788VV1wR0G2LiMi5KQjLJW/KlCl8//33XH311cybN48aNWqkOz40NDT57bHNjNy5c581Lb3xv+eSmJiImbFq1Sri4uJ8P/Hx8UyePNnv9SSt60wI96eu1C4OzJUrF4sXL2bx4sXUrVuXf//731SrVs03fCAxMZEnn3wyWb3r1q1j8+bNlChR4rzrPVNHIPchNYE61hc7NKJ06dLJxlsD/Pbbb4SGhib7kJXSP/7xDw4cOMD27dvZt28fnTt3BqBKlSoAfPnllzz99NOEhoYS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",
- "text/plain": [
- "
"
- ]
- },
- "metadata": {
- "needs_background": "light"
- },
- "output_type": "display_data"
- }
- ],
- "source": [
- "df.plot.bar(title=\"Comparing simulators and Aspen-M\", figsize=(12, 6))\n",
- "text = f\"f_free = {f_free_aspen:.3f} \\nf_simple_noise_model = {f_simple:.3f} \\nf_noise_model = {f_noisy_aspen:.3f}\"\n",
- "plt.text(1, 0.5, text, fontsize=14)\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "6da72e22-a594-41e7-a4e2-cb82cfe0726a",
- "metadata": {},
- "source": [
- "We see that compared to the full noise model, the simple model is less accurate, however, it is still a significant improvement over the noise-free case and has far fewer parameters in the model. "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b2c6b2ba-cb54-43f6-9a89-e29066b5192a",
- "metadata": {},
- "source": [
- "## Summary\n",
- "\n",
- "In this notebook, we showed how to construct a noise model for Rigetti Aspen-M-3 based only on the available calibration data.\n",
- "We used a coarse assumption of gate-independent single-qubit depolarizing noise and gate-dependant two-qubit noise. Our qubit-dependent model could be improved in many ways. We could add gate-dependence noise, or change the depolarizing channel to Pauli channels. "
- ]
- }
- ],
- "metadata": {
- "interpreter": {
- "hash": "a86a9ff96c3b20b9c94872c880ce00e370ced2a9959f9303c435918fb220e358"
- },
- "kernelspec": {
- "display_name": "conda_braket",
- "language": "python",
- "name": "conda_braket"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.5"
- },
- "toc-autonumbering": false,
- "toc-showcode": false,
- "toc-showmarkdowntxt": false,
- "toc-showtags": true
- },
- "nbformat": 4,
- "nbformat_minor": 5
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "74154c03-d48f-4f41-867b-5e30254ce31a",
+ "metadata": {},
+ "source": [
+ "# Noise models on Rigetti\n",
+ "\n",
+ "This notebook shows how to construct a noise model from device calibration data for Rigetti Ankaa-2. We compare the measurement outcomes of circuits run on a noisy simulator with the same circuits run on quantum processing units (QPUs), to show that simulating circuits with noise models more closely mimics QPUs.\n",
+ "\n",
+ "**Before you begin**: We recommend being familiar with [Noise models on Amazon Braket.](https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/braket_features/Noise_models/Noise_models_on_Amazon_Braket.ipynb)\n",
+ "Additionally, users should be familiar with [Running quantum circuits on QPU devices](https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/getting_started/2_Running_quantum_circuits_on_QPU_devices/2_Running_quantum_circuits_on_QPU_devices.ipynb). \n",
+ "\n",
+ "### Table of Contents\n",
+ "\n",
+ "- Noise model for Rigetti\n",
+ " - Loading device calibration data\n",
+ " - Comparing noisy simulator results to QPU results\n",
+ " - Smaller noise models compared to QPU results"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "e5e15fcd-55a2-4168-acc6-a38100f94511",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "from braket.aws import AwsDevice\n",
+ "from braket.circuits import Circuit, Gate, Observable\n",
+ "from braket.circuits.noise_model import GateCriteria, NoiseModel, ObservableCriteria\n",
+ "from braket.circuits.noises import AmplitudeDamping, BitFlip, Depolarizing, PhaseDamping, TwoQubitDepolarizing\n",
+ "from braket.devices import Devices, LocalSimulator"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d4da3e37-e93c-4273-98bc-d67b26b8c822",
+ "metadata": {},
+ "source": [
+ "Braket provides access to hardware providers' reported calibration data. \n",
+ "This can be used to construct noise models to approximate the behavior of the QPU when running circuits on a noisy simulator.\n",
+ "In this tutorial, we focus on local noise models with no crosstalk interactions. Real devices can have crosstalk and unexpected effects that can further degrade the results.\n",
+ "\n",
+ "The Ankaa-2 calibration data is available on the Braket devices page. Under qubit specs, the calibration data include the qubit index, with corresponding values for the $T_1$, $T_2$, fidelity from randomized benchmarking (fRB), fidelity from simultaneous randomized benchmarking (fsRB), and readout fidelity (fRO).\n",
+ "Under \"edge specs\", the data includes the RB fidelity for two qubit gates for each connected edge in the device topology."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "38001b96-faf7-49f0-b5a5-2d52820fcfc9",
+ "metadata": {},
+ "source": [
+ "**One-qubit calibration data (Qubit specs)**\n",
+ "\n",
+ "\n",
+ "\n",
+ "**Two-qubit calibration data (Edge specs)**\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "276d680f-887d-4929-bcaa-e276a6303496",
+ "metadata": {},
+ "source": [
+ "We can programmatically access all the calibration data with the Braket SDK. First we load the AwsDevice using the ARN for Rigetti Ankaa-2."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "a55ea9d5-05f3-4da5-a305-a391e914d012",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "rigetti = AwsDevice(Devices.Rigetti.Ankaa2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "66d8f3bf",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a8a42ae1-51ac-4bf1-bd6a-81ffd982b5f8",
+ "metadata": {},
+ "source": [
+ "The properties dictionary contains one- and two-qubit calibration data. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "153b5b63-8205-4a7e-bb18-0305ef428f36",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "one_qubit_data = rigetti.properties.standardized.oneQubitProperties\n",
+ "two_qubit_data = rigetti.properties.standardized.twoQubitProperties"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "661cca17-01fa-40be-bd3e-741bacc74ce4",
+ "metadata": {},
+ "source": [
+ "For Ankaa-2, we can get all qubit indices with `one_qubit_data.keys()` or with `rigetti.topology_graph.nodes`.\n",
+ "\n",
+ "The keys of the two qubit dictionary are the connected qubit pairs separated by a hyphen. \n",
+ "For example, if qubit 0 and 1 are connected the key is \"0-1\".\n",
+ "\n",
+ "\n",
+ "#### One-qubit noise \n",
+ "\n",
+ "Let's look at the one qubit calibration data for qubit 0."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "8d4ecc35-f4dc-49d8-b6ff-adaee560ac4b",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "OneQubitProperties(T1=CoherenceTime(value=8.645474408067754e-06, standardError=None, unit='S'), T2=CoherenceTime(value=1.2710111632672265e-05, standardError=None, unit='S'), oneQubitFidelity=[Fidelity1Q(fidelityType=FidelityType(name='RANDOMIZED_BENCHMARKING', description=None), fidelity=0.998913376867117, standardError=2.7016428586132974e-05), Fidelity1Q(fidelityType=FidelityType(name='SIMULTANEOUS_RANDOMIZED_BENCHMARKING', description=None), fidelity=0.9975435377199546, standardError=0.00015343141167926104), Fidelity1Q(fidelityType=FidelityType(name='READOUT', description=None), fidelity=0.9219999999999999, standardError=None)])"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "one_qubit_data[\"0\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4b676762-e642-4a98-89e3-86d79367071a",
+ "metadata": {},
+ "source": [
+ "For each qubit, there are various metrics of the quality:\n",
+ "\n",
+ "- **T1**: Thermal relaxation time is related to the time it takes for the excited state, |1⟩, to decay into the ground state, |0⟩. The probability of remaining in the excited state is $p(|1⟩)\\sim e^{-t/T_1}$\n",
+ "\n",
+ "- **T2**: The dephasing time, is the decay constant for the scale for a |+⟩ state to decohere into the completely mixed state. $p(|+⟩)\\sim e^{-t/T_2}$ \n",
+ "\n",
+ "- **Fidelity (RB)**: Single-qubit randomized benchmarking fidelities. RB fidelity quantifies the average gate fidelity where the average is over all Clifford gates. RB describes an *effective* noise model with gate-independent depolarizing noise on each Clifford gate.\n",
+ "\n",
+ "- **Fidelity (sRB)**: Single-qubit simultaneous randomized benchmarking fidelities. These are extracted by running single-qubit RB on all qubits simultaneously. Note that we expect the sRB fidelity to be lower than standard RB fidelity due to non-local crosstalk type noise on the device. \n",
+ "\n",
+ "- **Readout fidelity**: Single-qubit readout fidelities describes the probability of a bit flip error before readout in the computational basis. The readout fidelity is related to the probability of correctly measuring the ground state and excited states respectively, e.g. $f_{RO} =\\frac{p(0|0)+p(1|1)}{2}$"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "296708a3-771e-42b4-9ba8-f3db1a02970b",
+ "metadata": {},
+ "source": [
+ "Now that we know how to extract and use the calibration data, we can build a simple noise model. For every qubit we will add:\n",
+ "- amplitude dampening noise with probability $p= 1-e^{-t/T_1}$ for every gate\n",
+ "- phase dampening noise with probability $p= 0.5(1-e^{-t/T_2})$ for every gate\n",
+ "- depolarizing noise with probability $p=1-f_{sRB}$ (from simultaneous RB fidelity) for every gate\n",
+ "- readout bit flip noise with probability $p=1-f_{RO}$ to measurements \n",
+ "\n",
+ "Technically, the sRB fidelity already includes effects from $T_1$/$T_2$, however to be explicit we add these as separate terms. In a sense, this model might overestimate the noise on the QPU. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5769f2f4-de1c-4b47-9e4e-fd8d39b9e975",
+ "metadata": {},
+ "source": [
+ "To create the noise model, we iterate over all qubits keys in `one_qubit_data`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "7442cd27",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "noise_model = NoiseModel()\n",
+ "\n",
+ "# Readout Noise Model\n",
+ "for q, data in rigetti.properties.standardized.oneQubitProperties.items():\n",
+ " try:\n",
+ " readout_error = 1 - data.oneQubitFidelity[2].fidelity # readout\n",
+ " noise_model.add_noise(BitFlip(readout_error), ObservableCriteria(qubits=int(q)))\n",
+ " \n",
+ " depolarizing_rate = 1 - data.oneQubitFidelity[1].fidelity # SIMULTANEOUS_RANDOMIZED_BENCHMARKING\n",
+ " noise_model.add_noise(Depolarizing(probability=depolarizing_rate), GateCriteria(qubits=q))\n",
+ " except:\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "9d20d24c-17e2-4bba-b317-fb2d156bedb0",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Number of terms in noise model is: 167\n",
+ "Number of parameters in noise model is: 167\n"
+ ]
+ }
+ ],
+ "source": [
+ "num_params = sum(len(item.noise.parameters) for item in noise_model.instructions)\n",
+ "print(f\"Number of terms in noise model is: {len(noise_model.instructions)}\")\n",
+ "print(f\"Number of parameters in noise model is: {num_params}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "790c62f7-776a-4e4c-9405-7b7a14eee5b0",
+ "metadata": {},
+ "source": [
+ "#### Two-qubit noise \n",
+ "Next we consider adding two-qubit noise to the model. \n",
+ "\n",
+ "Let's first look at the data provided in the Ankaa-2 device calibration data. On the first connect, \"0-1\", the properties are:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "626cbe56-3649-48eb-a2a1-f669eb790b47",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "TwoQubitProperties(twoQubitGateFidelity=[GateFidelity2Q(direction=None, gateName='ISWAP', fidelity=0.9773180863130815, standardError=0.00408287380553746, fidelityType=FidelityType(name='INTERLEAVED_RANDOMIZED_BENCHMARKING', description=None))])"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "two_qubit_data[\"0-1\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4bcd3be4-a6e4-4cf3-841e-1a451e01803a",
+ "metadata": {},
+ "source": [
+ "Here, we see the fidelity per gate (ISWAP or CZ) and the associated standard error. \n",
+ "\n",
+ "Next we loop over the entries in the `two_qubit_data` dictionary and add two-qubit depolarizing noise to the model. Notice that Ankaa-2 has symmetric connections (\"0-1\" and \"1-0\") so we need to add noise in both directions."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "80f57550-6549-46b6-9a6d-ff7f0f03e90d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Two-qubit noise\n",
+ "for pair, data in two_qubit_data.items(): # iterate over qubit connections\n",
+ "\n",
+ " # parse strings \"0-1\" to integers [0, 1]\n",
+ " q0, q1 = (int(s) for s in pair.split(\"-\"))\n",
+ " try:\n",
+ " if data.twoQubitGateFidelity[0].gateName == \"ISWAP\":\n",
+ " phase_rate = 1 - data.twoQubitGateFidelity[0].fidelity\n",
+ " noise_model.add_noise(\n",
+ " TwoQubitDepolarizing(phase_rate),\n",
+ " GateCriteria(\n",
+ " Gate.ISwap, [(q0, q1), (q1, q0)]\n",
+ " ), # symmetric connections\n",
+ " )\n",
+ " except:\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "82376d08-84bc-4249-9c8d-d7aacdb73113",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Number of terms in noise model is: 274\n",
+ "Number of parameters in noise model is: 274\n"
+ ]
+ }
+ ],
+ "source": [
+ "num_params = sum(len(item.noise.parameters) for item in noise_model.instructions)\n",
+ "print(f\"Number of terms in noise model is: {len(noise_model.instructions)}\")\n",
+ "print(f\"Number of parameters in noise model is: {num_params}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5913a8a2-a9de-4e32-a35d-c48191891b1f",
+ "metadata": {},
+ "source": [
+ "### Compare circuits run on device vs simulator with a noise model\n",
+ "\n",
+ "Let's just look at the first 5 qubits. Note that to ensure the noise model applied T1 and T2 noise during the time between gate, we manually add identity gates to each moment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "6fabbd05-b480-43c6-b43a-d396d81bd687",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "T : │ 0 │ 1 │ 2 │\n",
+ " ┌──────────┐ ┌──────────┐ ┌───────┐ \n",
+ "q0 : ─┤ Rx(0.50) ├─┤ Rx(3.14) ├─┤ ISWAP ├─\n",
+ " └──────────┘ └──────────┘ └───┬───┘ \n",
+ " ┌──────────┐ ┌──────────┐ ┌───┴───┐ \n",
+ "q1 : ─┤ Rz(0.50) ├─┤ Rx(3.14) ├─┤ ISWAP ├─\n",
+ " └──────────┘ └──────────┘ └───────┘ \n",
+ " ┌──────────┐ ┌──────────┐ \n",
+ "q2 : ─┤ Rz(0.50) ├─┤ Rx(3.14) ├───────────\n",
+ " └──────────┘ └──────────┘ \n",
+ "T : │ 0 │ 1 │ 2 │\n"
+ ]
+ }
+ ],
+ "source": [
+ "np.random.seed(42)\n",
+ "\n",
+ "circ = Circuit().rx(0, 0.5).rz(1, 0.5).rz(2, 0.5).rx(0, np.pi).rx(1, np.pi).rx(2, np.pi).iswap(0, 1)\n",
+ "print(circ)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "e2af07ed-33de-4709-9e7c-5e3c3caaa751",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "T : │ 0 │ 1 │ 2 │\n",
+ " ┌──────────┐ ┌──────────┐ ┌───────┐ ┌─────────────┐ \n",
+ "q0 : ─┤ Rx(0.50) ├─┤ Rx(3.14) ├─┤ ISWAP ├─┤ DEPO(0.023) ├─\n",
+ " └──────────┘ └──────────┘ └───┬───┘ └──────┬──────┘ \n",
+ " ┌──────────┐ ┌──────────┐ ┌───┴───┐ ┌──────┴──────┐ \n",
+ "q1 : ─┤ Rz(0.50) ├─┤ Rx(3.14) ├─┤ ISWAP ├─┤ DEPO(0.023) ├─\n",
+ " └──────────┘ └──────────┘ └───────┘ └─────────────┘ \n",
+ " ┌──────────┐ ┌──────────┐ \n",
+ "q2 : ─┤ Rz(0.50) ├─┤ Rx(3.14) ├───────────────────────────\n",
+ " └──────────┘ └──────────┘ \n",
+ "T : │ 0 │ 1 │ 2 │\n"
+ ]
+ }
+ ],
+ "source": [
+ "noisy_circ = noise_model.apply(circ)\n",
+ "\n",
+ "print(noisy_circ)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "9304dcf1-a1e8-4e41-a6b5-769841f4eefe",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "simulator = LocalSimulator() # noise free simulator\n",
+ "task = simulator.run(circ, shots=10_000)\n",
+ "free_probs = task.result().measurement_probabilities"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "0e1d9cfe-03d6-406b-b4ab-89166a907681",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "noisy_simulator = LocalSimulator(\"braket_dm\")\n",
+ "noisy_task = noisy_simulator.run(noisy_circ, shots=10_000)\n",
+ "noisy_probs = noisy_task.result().measurement_probabilities"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b3f370bd-a8b3-4d5b-a724-da549fc4c81f",
+ "metadata": {},
+ "source": [
+ "
\n",
+ "Note: The below section runs tasks on the Rigetti Ankaa-2 device. When you run this notebook, make sure the device is currently available. You can find QPU availability windows on the Devices page in the Amazon Braket Console.\n",
+ "
\n",
+ "\n",
+ "
\n",
+ "Note: Running the circuit below will result in charges on your AWS account.\n",
+ "