How to visualize quantum circuit diagrams with Q#
Quantum circuit diagrams are a visual representation of quantum operations. They show the flow of qubits through the quantum program, including the gates and measurements applied to them.
In this article, you'll learn how to visually represent quantum algorithms with quantum circuit diagrams using Visual Studio Code or Jupyter Notebooks.
For more information about quantum circuit diagram conventions, see Quantum circuits conventions.
Prerequisites
VS Code
The latest version of Visual Studio Code or open VS Code on the Web.
The latest version of the Azure Quantum Development Kit extension.
The latest Azure Quantum
qsharp
andazure-quantum
Python packages.python -m pip install --upgrade qsharp azure-quantum
Jupyter Notebooks
The latest version of Visual Studio Code or open VS Code on the Web.
VS Code with the Azure Quantum Development Kit, Python, and Jupyter extensions installed.
The latest Azure Quantum
qsharp
andqsharp-widgets
packages, and theipykernel
package.python -m pip install --upgrade qsharp qsharp-widgets ipykernel
Quantum circuits with Visual Studio Code
Follow these steps to visualize quantum circuits of Q# programs in Visual Studio Code. For more information about quantum circuit diagram conventions, see Quantum circuits conventions.
Viewing circuit diagrams for a Q# program
Open a Q# file in Visual Studio Code, or load one of the quantum samples.
To visualize the quantum circuit of the Q# program, select View -> Command Palette and type “circuit” which should bring up the Q#: Show circuit option. You can also click on Circuit from the list of commands preceding
Main()
.The circuit is displayed in the Q# circuit window. For example, the following circuit corresponds to an operation that put a qubit in a superposition and then measures it. The circuit diagram shows one qubit register which it's initialized to the |0⟩ state. Then, a Hadamard gate, H, is applied to the qubit, followed by a measurement operation, which is represented by a meter symbol.
Viewing circuit diagrams for operations
You can visualize the quantum circuit for a single Q# operation. To do this, click on the Circuit button in the code lens that appears above the operation declaration.
Viewing circuit diagrams when debugging
When debugging a Q# program, you can visualize the quantum circuit based on the current state of the program.
Select the Debug button from the list of code lens commands preceding
Main()
.In the Run and debug view on the left side, expand the Quantum Circuit section under Variables pane to show the circuit as you step through the program.
You can step through the code and set breakpoints in various points to see the circuit update as the program is run.
The current quantum circuit is shown in the Q# Circuit panel. This circuit diagram represents the current state of the simulator, that is, the gates that have been applied up until the current point of execution.
Quantum circuits with Jupyter Notebooks
In Jupyter Notebooks, you can visualize quantum circuits using the qsharp-widgets
package. This package provides a widget that renders a quantum circuit diagram as an SVG image.
In Visual Studio Code, select View > Command palette and select Create: New Jupyter Notebook.
In the first cell of the notebook, run the following code to import the Q# module.
import qsharp
Add a new cell and enter the Q# code. For example, the following code prepares a Bell State.
%%qsharp // Prepare a Bell State. use register = Qubit[2]; H(register[0]); CNOT(register[0], register[1]);
You can use the
dump_circuit()
function to display a quantum circuit based on the current state of the program. For example, the circuit diagram shows two qubit registers which are initialized to the |0⟩ state. Then, a Hadamard gate, H, is applied to the first qubit. After that, a CNOT gate is applied using the first qubit as control, which is represented as a dot, and the second qubit as target, which is represented as an X.qsharp.dump_circuit()
q_0 ── H ──── ● ── q_1 ───────── X ──
You can visualize quantum circuits as an SVG image by using the
qsharp-widgets
package. In this case, the CNOT gate is represented as a line connecting the two qubits, with a dot on the control qubit and a circumscribed cross on the target qubit. For more information, see Quantum circuits conventions.from qsharp_widgets import Circuit Circuit(qsharp.dump_circuit())
Viewing circuit diagrams for an entry expression
You can generate a circuit diagram for any program with an entry expression by calling qsharp.circuit()
and passing the entry expression as an argument.
For example, add a new cell and copy the following code, which prepares a GHZ state.
%%qsharp import Std.Diagnostics.*; import Std.Measurement.*; operation GHZSample(n: Int) : Result[] { use qs = Qubit[n]; H(qs[0]); ApplyToEach(CNOT(qs[0], _), qs[1...]); let results = MeasureEachZ(qs); ResetAll(qs); return results; }
Add a new cell and run the following code to visualize the circuit. For example, prepare a GHZ state with 3 qubits.
Circuit(qsharp.circuit("GHZSample(3)"))
Viewing circuit diagrams for operations with qubits
You can generate circuit diagrams for any operation that takes qubits or arrays of qubits. The diagram shows as many wires as there are input qubit, plus any additional qubits that are allocated within the operation. When the operation takes an array of qubits (Qubit[])
, the circuit shows the array as a register of 2 qubits.
Add a new cell and copy the following example. This code prepares a cat state.
%%qsharp operation PrepareCatState(register : Qubit[]) : Unit { H(register[0]); ApplyToEach(CNOT(register[0], _), register[1...]); }
Add a new cell and run the following code to visualize the circuit of the
PrepareCatState
operation.Circuit(qsharp.circuit(operation="PrepareCatState"))
Conditions that affect circuit diagrams
When visualizing quantum circuits, the following conditions can affect the visualization of the circuit diagram.
Dynamic circuits
Circuit diagrams are generated by executing all the classical logic within a Q# program and keeping track of any qubits that have been allocated or gates that have been applied. Loops and conditionals are supported as long as they only deal with classical values.
However, programs that contain loops and conditional expressions that use qubit measurement results are trickier to represent with a circuit diagram. For example, an expression like the following
if (M(q) == One) {
X(q)
}
cannot be represented with a straightforward circuit diagram, since the gates are conditional on a measurement result. Such a circuit is called a dynamic circuit.
Circuit diagrams can be generated for dynamic circuits by running the program in the quantum simulator, and tracing the gates as they are applied. This is called trace mode, as the qubits and gates are being traced as simulation is being performed.
The downside of traced circuits is that they only capture the measurement outcome, and the consequent gate applications, for a single simulation. In the above example, if the measurement outcome is Zero
, you don't see the X
gate in the diagram. Another run of the simulation may show a slightly different circuit.
Target profile
The currently selected target profile influences how circuit diagrams are generated. Target profiles are used to specify the capabilities of the target hardware, and the restrictions that are imposed on the quantum program.
When the target profile is set to Unrestricted or QIR Adaptive RI, the circuit diagrams show the quantum operations that are invoked in the Q# program. When the target profile is set to QIR base, the circuit diagrams show the quantum operations that would be run on hardware if the program is submitted to Azure Quantum with this target profile.
Note
To select the target profile in VS Code, select View -> Command Palette and select Q#: Set the Azure Quantum QIR target profile. You can select
QIR base
,QIR Adaptive RI
, orunrestricted
from the dropdown list.To select the target profile in Python, call
qsharp.init(target_profile=qsharp.TargetProfile.Base)
,qsharp.init(target_profile=qsharp.TargetProfile.Adaptive_RI)
orqsharp.init(target_profile=qsharp.TargetProfile.Unrestricted)
.
Specifically, gate decompositions are applied that would make the resulting circuit compatible with the capabilities of the target hardware. These are the same decompositions that would get applied during code generation and submission to Azure Quantum.
For example, consider the following Q# program that measures a qubit and an array of qubits.
import Std.Measurement.*; operation Main() : (Result, Result[]) { // The `M` operation performs a measurement of a single qubit in the // computational basis, also known as the Pauli Z basis. use q = Qubit(); let result = M(q); Reset(q); // The `MeasureEachZ` operation measures each qubit in an array in the // computational basis and returns an array of `Result` values. use qs = Qubit[2]; let results = MeasureEachZ(qs); return (result, results); }
When target profile is set to Unrestricted or QIR Adaptive RI, the gates displayed on the circuit correspond exactly to the quantum operations that are invoked in the Q# program.
When the target profile is QIR base, the circuit looks different. Since Base profile targets don't allow qubit reuse after measurement, the measurement is now performed on an entangled qubit instead. Since
Reset
operation isn't a supported gate in Base Profile, it's dropped. The resulting circuit matches what would be run on hardware if this program is submitted to Azure Quantum with this target profile.