Path: blob/main/qec101/Solutions/03_QEC_Noisy_Simulation_Solution.ipynb
1132 views
Lab 3 - Simulating Quantum Noise
Overview
Noise is the enemy of useful quantum computing and the reason quantum error correction (QEC) is necessary in the first place. Noise from either the environment or the unavoidable imperfections of device controls produce errors that corrupt the quantum information stored on the qubits and ruin algorithm results.
Though all QPUs share this reality, each QPU exhibits a unique noise profile depending on its qubit modality and a variety of other design factors. Understanding a device's noise is critical for guiding algorithm development, discovery of new QEC techniques, and improvements to the hardware itself.
Simulating noisy QPUs can be extremely helpful for this task and complements the valuable but limited experimental data. For example, researchers can train QEC methods that rely on AI with massive simulated data sets. Similarly, insights from simulation can inform design improvements. Simulation can also be used to model the physics of the individual qubits and identify specific sources of noise, much like NVIDIA uses GPUs to simulate digital twins for next generation GPUs in the design process.
This lab will provide an overview of simulating noisy quantum circuits. You will learn how to use CUDA-Q to perform a number of different simulations and apply the results to different uses cases.
Prerequisites: Learners should have familiarity with Jupyter notebooks and programming in Python and CUDA-Q. It is assumed the reader has some familiarity already with quantum computation and is comfortable with braket notation and the concepts of qubits, quantum circuits, measurement, and circuit sampling. The CUDA-Q Academic course entitled "Quick Start to Quantum Computing with CUDA-Q" provides a walkthrough of this prerequisite knowledge if the reader is new to quantum computing and CUDA-Q or needs refreshing. Learners would also benefit from completing the first two notebooks in this series The Basics of Classical and Quantum Error Correction and Stabilizers, the Shor code, and the Steane code.
The list below outlines what you'll be doing in each section of this lab:
3.1 Define a quantum noise channel, the density matrix, and Kraus operators
3.2 Learn two ways to simulate noise: density matrix and trajectory simulation
3.3 Lean how to use noisy simulation for three different uses cases
3.3a Study the impact of noise on a standard quantum chemistry algorithm
3.3b Use noise modeling to implement a noise mitigation technique
3.3c Run QEC experiments with noise models
3.4 Perform dynamical simulation of a qubit to build a noise model for quantum circuit simulation
Terminology and notation you'll use
noise channel, density matrix, trajectory simulation, density matrix simulation, Kraus operator
quantum error mitigation, zero noise extrapolation
circuit-level noise experiments
dynamical simulation, amplitude damping
Before we get started, excecute the cells below to load the necessary packages.
3.1 Quantum Noise Channels ##
In the first lab of this series, the concept of a noise channel was introduced. A noise channel is a mathematical model used to describe how a quantum state is impacted by the presence of noise.

A noise channel can correspond to application of a gate to physical qubits, a qubit's interaction with another nearby qubit, or simply the passage of time and the resulting decay of the quantum state as it interacts with anything else from the environment. QEC is a promising solution to this problem as a logically encoded quantum state can go through the noise channel, impacting each data qubit, while providing a means for the original state to be restored.

However, as previous labs have emphasized, QEC is hard to implement, and the development of new QEC protocols is still an active research field. In practice, experimental data obtained from the QPU can help measure quantities like gate fidelity and inform a noise model which captures all of the noise channels present in the device. This noise model can then be used to simulate data that emulates the performance of the QPU.
There are many practical benefits to this that will be explored in this lab. A recent example of this is NVIDIA's work with QuEra to build an AI decoder. Training this model required a massive amount of data which could be obtained efficiently via simulation. Noisy circuit simulation allowed for millions of syndromes to be obtained with their associated errors, something not possible to do with experimental data.
The Density Matrix
Before discussing some of the ways to simulate noise, it is necessary to take a step back and consider representation of a quantum state using the density matrix. The density matrix () is a mathematical object that completely describes a quantum state and has the following properties.
Its trace (sum of the diagonal elements) is equal to 1.
It is Hermitian:
It is positive semi-definite. (All eigenvalues are positive.)
If a quantum system is in one of a any quantum states with probability , then the density matrix is defined as a linear combination of outer products of those states with probability coefficients:
Exercise 1:
use CUDA-Q's function and the density matrix simulator (more on that later) to produce any three qubit density matrix. Write code to check that the three properties listed above are met. Make sure to set tolerances on these checks so that, for example, an eigenvalue of zero is not wrongfully flagged as `-1.2e-20`.
Statevectors correspond to pure states, while the density matrix can describe mixed states, that is an overall state composed of a combination of pure states.
A state is considered pure if the trace of is equal to 1.
This can be a bit confusing because a pure state can actually be a superposition state and a mixed state can be a combination of two states that do not describe superpositions. The following exercise will make this more clear.
Exercise 2 :
Consider the density matrix .
Using CUDA-Q build kernels for the state and the state, using these kernels and the command define the density matrix , and compute trace(). Is the state pure?
Now, code a Bell state and do the same thing with its density matrix. Is it a pure state?
A mixed state means that there is classical uncertainly about which quantum state defines the system, even if both quantum states are deterministic like and . However, a bell state is pure, meaning that the overall quantum state is known with certainly, even if the state describes a superposition with inherent uncertainty. Another key term is completely mixed state, which refers to a density matrix where all of the eigenvalues are the same, meaning the density matrix describes the state with the theoretical maximum of uncertainty.
Kraus Operators
Now, why the business about density matrices? The answer is that a noise channel needs to be an effective model that can generalize to impact mixed states. In fact, many noise channels will produce a mixed state from a pure state.
Mathematically this is done with Kraus operators () that evolve the density matrix as the state proceeds through a noisy channel .
Kraus operators have the condition that so the trace of the density matrix is preserved.
For example, a valid set of operators is and which will perform a bitflip error with probability and apply the identity (no change) with probability . Let's apply this to the density matrix, , for the state. The result becomes . Notice the result is now mixed state.
The table below summarizes some of the channels included in CUDA-Q which you will use in later exercises. Notice too, that each noise channel can be geometrically represented as a deformation of the Bloch sphere.

By applying any number of Kraus operators to the density matrix, it is possible to evolve it and sample the resulting state to determine how noise impacts the output. This is easily accomplished in CUDA-Q with the density-matrix-cpu backend. You can then build a noise model consisting of noisy channels applied to specific gate operations with select probabilities. The exercise below will get you started with the syntax.
Exercise 3 :
You will be using CUDA-Q's built in noise channel tools throughout this lab. Get a sense for how it works by building a two qubit kernel and perform an operation on each qubit. Edit the code block below to build a noise model consisting of two bitflip channels with probabilities of .10 and .25 on the gate for qubit 0 and 1, respectively. Does the sample distribution agree with what you would expect?
3.2 Two Ways to Simulate Noise
Density matrix simulation can produce exact results with the quality of simulation limited only by the accuracy of the underlying noise model. Unfortunately, density matrix simulation is expensive and requires storage of the entire matrix, limiting it to a smaller number of qubits.
This scalability problem can be circumvented with a method called trajectory based simulation which allows for approximate noise simulation at much larger scales. Unlike density matrix simulation that applies Kraus operators to every state, trajectory based simulation assumes the Kraus operators occur as a Markov process.
The assumption of a Markov process is that the application of each Kraus operator is independent from the others. This is usually a reasonable assumption as a physical QPU might, for example, only apply gates in an isolated gate zone.
The code blocks below will make it clear how the two approaches differ. Consider a very basic circuit that prepares the state with bitflip errors on each qubit corrupting the result. First, run the cell below. Notice that get_state returns the same density matrix each time you run the code. This density matrix describes the mixture of all possible pure states and returns the sample distribution below.
Trajectory based simulation can run in CUDA-Q by simply changing the target to nvidia. If the kernel below had no noise, the statevector (output from get_state) should be [0,0,0,0,0,0,0,1] corresponding to the state. When sampling is performed with the trajectory based simulator, the Kraus operators are applied based on their probabilities to produce a new state vector for each shot. The widget below allows you to explore the possible outcomes and their associated probabilities.
Try running the CUDA-Q simulation above with two or three different bitflip error probabilities and set the slider below to match. Confirm that the results from the density matrix simulations above match the expected distribution from the trajectory-based approach. You will need to move the Images > noisy > trajectory_widget.py file from the CUDA-Q Academic github repository into your working directory to execute this optional cell.
Running the code below, notice get_state produces a different state vector each time. Because the number of possible trajectories is small, trajectory based sampling can reproduce the same distribution that would be obtained from density matrix simulation.
⚠️
Just a heads-up: The rest of this notebook is designed to be run on an environment with a GPU. If you don't have access to a GPU, feel free to read through the cells and explore the content without executing them. Enjoy learning!
⚠️
Another benefit of trajectory based simulation is that it can be used with tensor network based simulators to simulate circuits that would be far too large for density matrix or statevector simulation. CUDA-Q can run exact tensor network or approximate Matrix Product State (MPS) simulations with trajectory based simulation to simulate systems of hundreds to thousands of qubits.
Clever sampling algorithms can also be used to filter trajectories and exclude certain types of errors or focus on sampling only a subset of the most likely errors. A recent paper published by NVIDIA researchhttps://arxiv.org/pdf/2504.16297 explains this in greater detail and described how methods like this can sample trillions of noisy samples in just a few hours using an AI supercomputer. This is extremely helpful for training AI QEC decoders where experimental data cannot be obtained in sufficient volume.
3.3 Use cases for Noisy Simulations
This section will explore three use cases of noisy simulation used to model the impact of noise patterns on algorithms, perform quantum error mitigation, and run QEC experiments.
3.3a: Understanding How Noise Impacts Algorithm Results ##3
A natural application of noisy simulation is to explore how different noise patterns might impact the results of an algorithm. Such simulations can be beneficial for a number of reasons. This section along with the following two will explore three use cases for noisy circuit simulation.
The first use case is to better understand how device noise impacts the outcome of an algorithm. Researchers can use these sorts of results to produce insights that guide compilation methods by identifying how particular algorithms might be more or less sensitive to particular noise channels. Such an approach is also useful to develop noise models by tuning them to agree with the results obtained running the same application experimentally.
This section will walk you through an exercise to explore the impact of noise on a standard chemistry experiment. The CUDA-Q Solvers library makes it easy to prepare a quantum circuit to compute the ground state energy of a molecule. The code section below prepares a circuit with the standard UCCSD ansatz as well as the Hamiltonian of the hydrogen molecule. Run the cell below to get the noiseless energy and see a print out of the circuit. Note: the circuit parameters are not optimized, but this does not matter as it is just a reference point to study the impact of noise.
As a bonus, try repeating this exercise using a simulator tuned to mimic the noise of a physical QPU. CUDA-Q provides access to, for example, the Quantinuum H-2 emulator, IonQ's emulator, Infleqtion's noisy simulator, and more.
Exercise 4 :
Now, write a function that computes the expectation values for various configurations of errors. The function comments will guide you on the inputs and what the function should return.
The function below, will take the result from get_noisy_data and plot them. Just enter each output from get_noisy_data as an element of the datasets list variable, name the categories for the x-axis, and label the datasets if more than one is provided. The next cell provides an example.
Analyzing Shot Number
A source of noise not discussed thus far, and of a completely different nature than any physical noise channel, is sampling error. Sampling based quantum algorithms can produce inaccurate results simply due to sampling error, even if the hardware were perfect. The code below, demonstrates how to use the plot_data function, and produces the distributions of hydrogen ground state energies obtained with anywhere from 10 to 10000 shots compared to 0 which is the noiseless result in the limit of infinite samples.
Notice how sampling based error is centered on the noiseless result and rapidly dissipates as more shots are used. The rest of the simulations below will not perform shot based sampling so the only deviations from zero are due to the noise channels you implement below. Nevertheless, it is important to not forget that sampling error is a ubiquitous source of error for most quantum algorithms.
Analyzing Trajectory Number
Similar to sampling error, trajectory based noise simulation is also dependent on the number of trajectories used to compute the expectation value. Even if each trajectory is used to compute the expectation value exactly, if too few statevectors are sampled, the results of the noisy simulations can become unreliable.
Build a data set that only applies bitflip errors on gates for every qubit with probability 0.01. Vary the trajectories from 10 to 10000 and comment on the results.
Does this noise model systematically over or under estimate the energy prediction? Could you be confident in this observation if you only used 10 trajectories?
Analyzing Error Probability and Gate Type
Now, turning to questions more specific to the structure of this algorithm's circuit, one can ask how changing the probability of a bitflip error impacts the computed energy. Plot the results corresponding to bitflip errors on all gates for all qubits with decreasing probabilities of .1, .01, .001, .0001. Plot a second series on the same graph but this time have the bitflip error applied on the gates.
For which type of gate are bitflip errors more problematic to the result?
Analyzing Error Type
Now, fix the error probability at 0.1 and this time vary the type of error. Keep the two series with errors occurring on and gates.
Which type of error is most severe? Are there any errors that have little to no impact?
Analyzing Error Location (Qubit)
Finally, keep the two series and now perform only Amplitude Damping errors. This time, set all of the probabilities equal to 0, expect for the single error prone qubit which is set to a probability of 0.1.
If you had a QPU where you knew qubit A was particularly prone to amplitude damping for some reason, when you compile the algorithm, which wire of the quantum circuit (q0, q1, q2, q3) should map to qubit A to ensure the best results based on your simulations?
3.3b: Zero Noise Extrapolation
QPU results today are sometimes improved using quantum error mitigation (QEM) techniques. QEM techniques use classical postprocessing to improve results without the utilization of proper QEC protocols. One such QEM technique is zero noise extrapolation (ZNE). The idea behind ZNE is that it is really hard to remove noise from an algorithm run on a physical QPU, but it is very easy to add noise.
The ZNE process works by applying increasing factors of error through clever application of the identity operator. For example, consider a circuit composed of a single layer of rotations of radians. Applying the same gate three times is mathematically the same as applying it once and should have no impact on the result.
Experimentally, this is truly the identity operation as each gate is a noise channel and the total noise factor is increased from 1x to 3x. If this procedure is continued (5x, 7x, 9x, ...) the data can be fit to a curve and extrapolated back to estimate the experimentally inaccessible case of a 0x noise factor! So, paradoxically, adding noise can improve the result.

ZNE is a useful technique because it can be used experimentally. Noisy circuit simulation can demonstrate its effectiveness and help benchmark the effectiveness of ZNE when used on a physical QPU, help refine noise models, and test other QEM techniques before running experiments.
Exercise 5 :
You will now code an ZNE example by following the steps below:
Create a Random Hamiltonian for a larger (20 qubit circuit)
Define a quantum circuit with a layer of gates followed by a layer of gates.
Put a bitflip error on the gates and an Amplitudes Damping error on the gates.
Perform ZNE to obtain a correction for each. (Hint: use the to fit a polynomial.)
Apply the correction to the original noisy circuit and calculate the percent error of the noisy circuit and the ZNE corrected result relative to the noiseless case.
3.3c: QEC Experiments
Noisy circuit simulation is perhaps most useful as a tools for QEC researchers. One can test how a code will perform in a variety of different noise conditions. Assuming an accurate noise model, this can be a great way to assess characteristics of new codes. Below you will add noise to the Steane code you prepared in lab 2.
Exercise 6 :
Apply noise to the Steane code in the following three ways and determine which case produces the best and worst logical error rates, keeping the probability of error fixed at 0.05. In which cases is the logical error rate an improvement over the 0.05 error rate? 1. Use to manually apply Kraus operators following encoding of the Steane code but before the stabilizer checks are run. These errors are not tied to gates but model errors induced while the system idles. 2. Now, use to apply a depolarization error following all of the two qubit gates in the encoding circuit, whee q and r are the two qubits involved in the gate operation. 3. Apply a bitflip noise channel to all measurements. In this case, errors are also possible in measurements performed on the ancillas. This helps model situations where measurements are performed in a way that is not fault tolerant.
3.4: Using Dynamical Simulations to Build a Noise Model
The noise models used thus far are meant to mimic the underlying physics of physical qubits. Often, noise models are heavily informed by experiment, but extracting meaningful insights can be extremely difficult for such complex systems.

To help with this task, the physics of the qubits can also be simulated to better understand noise sources and improve interpretation of experimental data. This sort of simulation is known as dynamical simulation and models the evolution of a quantum system over time as the system interacts with its environment.
Exercise:
The code below will help you walk through an example of using dynamical simulation to produce a noise model for a single qubit amplitude damping channel. Recall, the corresponding noise channel looks like this.
Thus, the goal is to simulate a simple qubit system to determine what , the probability of energy loss resulting in decay to the ground state, is.
Dynamical simulation is its own topic that warrants a detailed introduction that will not be provided here. Instead, the steps of the dynamical simulation will be discussed at a high level while curious readers can explore the CUDA-Q dynamics page for more information and more detailed examples.
To get started, import the following functions and libraries. This example will use the CUDA-Q dynamics backend, set like any other backend.
You will simulate a superconducting transmon qubit which is driven close to resonance, to produce so called Rabi oscillations. You will model a qubit which has a set resonant frequency, add a driving term that depends on time and will drive the system close to its resonant frequency, and by doing so, introduce a change in population from the state to the state. In other words, you will simulate thr underlying physics required to perform an gate.
The first step is to construct the Hamiltonian of the system. The Hamiltonian consists of a term that encodes the resonant frequency for the qubit corresponding to the transition from the to the state. The second, is a driving term that applies a time dependent function towards the qubits resonant frequency. When this happens, Rabi oscillations occur and the population of the system changes from 100% to 100% , that is, an gate applied to the qubit!
Exercise 7 :
The code below sets up the the problem Hamiltonian, defines the dimensions of the system and specifies the initial ground state. The terms have more meaning than described above, but their details are not relevant for the purposes of this exercise.
Dynamics simulations are performed numerically and require a time step specifying how the evolution operator is applied. For this problem it is setup below.
You are now ready to run the simulation using CUDA-Q's evolve function, which does require GPU access. The code cell below shows the baseline case where the qubit does not interact with its environment (i.e. there is no decoherence), and the plot shows a the probability of sampling the state rise from 0 to 1. This corresponds to a pulse used to implement a perfect noiseless gate.
Now, things can get a bit more interesting when decoherence is factored in. In this case, a so called collapse operator is added to the dynamical simulation to model the decay of energy into the environment as the system evolves. In this case the simple collapse operator np.sqrt(gamma_sm) * spin.minus(0) is added. gamma_sm is set to 1, but would in practice be determined by some experimental quantity. Now, by running this simulation, you will be able to simulate amplitude damping and see what the probability of the state remains after the pulse.
The pulse now has a peak of around .85, meaning is a reasonable choice to parameterize an amplitude damping channel.
This is a very simple example, and in practice it is much harder to derive noise models from dynamical simulations. Nevertheless, they are powerful tools for understanding noise. You can also tweak other aspects of the simulation for more complex situations. For example, try the following and see how they might change the amplitude damping parameterization.
change
omega_drive = 0.95 * omega_zto be close to but not the same as the resonance frequency.Add 0.1 to
t_final = np.pi / omega_xto test what would happen if a gate pulse is applied for too long.
Summary
Noise is the greatest challenge facing quantum computers. Accurate simulations can help us understand both the sources and impacts of noise to guide development of better hardware, algorithms, and QEC codes. You now know how to utilize CUDA-Q for noise modeling as well as a number of situations where noise modeling is useful. Scaling up any of these examples makes simulation much more challenging and requires the power of CUDA-Q and AI supercomputing to usher in new advancements to the field.