{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# QDK Visualization Tools\n", "\n", "This tutorial introduces you to a variety of tools offered by the Microsoft Quantum Development Kit to visualize various elements of Q# programs. \n", "These tools are extremely helpful both for learners seeking to confirm their understanding of the behavior of the program and for experienced quantum software developers debugging their programs. \n", "\n", "This tutorial covers the following topics:\n", "* Displaying the quantum state of the program: DumpMachine and DumpRegister.\n", "* Displaying the matrix implemented by the quantum operation: DumpOperation.\n", "* Drawing the circuit of an execution path of the program: %trace.\n", "* Running through the quantum program step-by-step: %debug." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Display the quantum state of a single-qubit program\n", "\n", "Let's start with a simple scenario: a program that acts on a single qubit. \n", "The state of the quantum system used by this program can be represented as a complex vector of length 2, or, using Dirac notation,\n", "\n", "$$\\begin{bmatrix} \\alpha \\\\ \\beta \\end{bmatrix} = \\alpha|0\\rangle + \\beta|1\\rangle$$\n", "\n", "> If you need a refresher on single-qubit quantum systems and their states, please see the tutorial on [the concept of a qubit](../Qubit/Qubit.ipynb).\n", "\n", "If this program runs on a physical quantum system, there is no way to get the information about the values of $\\alpha$ and $\\beta$ at a certain point of the program execution from a single observation. \n", "You would need to run the program repeatedly up to this point, perform a measurement on the system, and aggregate the results of multiple measurements to estimate $\\alpha$ and $\\beta$.\n", "\n", "However, at the early stages of quantum program development the program typically runs on a simulator - a classical program which simulates the behavior of a small quantum system while having complete information about its internal state. \n", "You can take advantage of this to do some non-physical things, such as peeking at the internals of the quantum system to observe its exact state without disturbing it!\n", "\n", "[DumpMachine](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.diagnostics.dumpmachine) function from [Microsoft.Quantum.Diagnostics namespace](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.diagnostics) allows you to do exactly that. This function is available in standalone Q# applications as well.\n", "\n", "### Demo: DumpMachine for single-qubit systems\n", "\n", "The following demo shows how to use `DumpMachine` to output the state of the system at any point in the program without affecting the state.\n", "\n", "> Note that the Q# code doesn't have access to the output of `DumpMachine`, so you cannot write any non-physical code in Q#!\n", "\n", "The output of `DumpMachine` is accurate up to a global phase: sometimes you'll see that all amplitudes are multiplied by some complex number compared to the state you're expecting." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "// Run this cell using Ctrl+Enter (⌘+Enter on Mac)\n", "// Then run the next cell to see the output\n", "\n", "open Microsoft.Quantum.Diagnostics;\n", "\n", "operation SingleQubitDumpMachineDemo () : Unit {\n", " // This line allocates a qubit in state |0⟩.\n", " use q = Qubit();\n", " Message(\"State |0⟩:\");\n", "\n", " // This line prints out the state of the quantum system.\n", " // Since only one qubit is allocated, only its state is printed.\n", " DumpMachine();\n", "\n", " // This line changes the qubit to state |+⟩ = (1/sqrt(2))(|0⟩ + |1⟩).\n", " // 1/sqrt(2) is approximately 0.707107.\n", " H(q);\n", "\n", " Message(\"State |+⟩:\");\n", " DumpMachine();\n", "\n", " // This will put the qubit into an uneven superposition,\n", " // where the amplitudes of |0⟩ and |1⟩ have different absolute values and relative phases.\n", " Rx(1.0, q);\n", " Ry(2.0, q);\n", " Rz(3.0, q);\n", "\n", " Message(\"Uneven superposition state:\");\n", " DumpMachine();\n", "\n", " // This line returns the qubit to state |0⟩ before releasing it.\n", " Reset(q);\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%simulate SingleQubitDumpMachineDemo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise 1: Learn the state of the qubit without measurements\n", "\n", "**Input:** A qubit in an unknown state $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$. The amplitudes $\\alpha$ and $\\beta$ will be real and non-negative.\n", "\n", "**Output:** A tuple of two numbers $(\\alpha', \\beta')$ - your estimates of the amplitudes $\\alpha$ and $\\beta$.\n", "The absolute errors $|\\alpha - \\alpha'|$ and $|\\beta - \\beta'|$ should be less than or equal to 0.001.\n", "\n", "The test will call your code exactly once, with the fixed state parameter that will not change if you run the cell several times." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%kata T1_LearnSingleQubitState\n", "\n", "operation LearnSingleQubitState (q : Qubit) : (Double, Double) {\n", " // ...\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Display the quantum state of a multi-qubit program\n", "\n", "Now let's take a look at the general case: a program that acts on $N$ qubits. \n", "The state of the quantum system used by this program can be represented as a complex vector of length $2^N$, or, using Dirac notation,\n", "\n", "$$\\begin{bmatrix} x_0 \\\\ x_1 \\\\ \\vdots \\\\ x_{2^N-1}\\end{bmatrix} = \\sum_{k = 0}^{2^N-1} x_k |k\\rangle$$\n", "\n", "Same as in the single-qubit case, `DumpMachine` allows you to see the amplitudes $x_k$ for all basis states $|k\\rangle$ directly.\n", "\n", "> Note the use of an integer in the ket notation instead of a bit string with one bit per qubit. \n", "By default, `DumpMachine` uses little-endian to convert bit strings to integers in the ket notation.\n", "For more details on endiannes, see [the multi-qubit systems tutorial](../MultiQubitSystems/MultiQubitSystems.ipynb#Endianness).\n", "\n", "### Demo: DumpMachine for multi-qubit systems" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "open Microsoft.Quantum.Diagnostics;\n", "\n", "operation MultiQubitDumpMachineDemo () : Unit {\n", " // This line allocates two qubits in state |00⟩.\n", " use qs = Qubit[2];\n", " Message(\"State |00⟩:\");\n", "\n", " // This line prints out the state of the quantum system.\n", " DumpMachine();\n", "\n", " // X gate changes the second qubit into state |1⟩.\n", " // The entire system is now in state |01⟩, or, in little-endian notation, |2⟩.\n", " X(qs[1]);\n", " Message(\"State |01⟩:\");\n", " DumpMachine();\n", "\n", " CNOT(qs[1], qs[0]);\n", " Rx(1.0, qs[0]);\n", " Ry(2.0, qs[1]);\n", " Rz(3.0, qs[1]);\n", "\n", " Message(\"Uneven superposition state:\");\n", " DumpMachine();\n", "\n", " // This line returns the qubits to state |0⟩ before releasing them.\n", " ResetAll(qs);\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "%simulate MultiQubitDumpMachineDemo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise 2: Learn the amplitude of a basis state without measurements\n", "\n", "**Input:** 2 qubits in an unknown state $|\\psi\\rangle = \\sum_{k = 0}^{3} x_k |k\\rangle$. The amplitudes $x_k$ will be real and non-negative.\n", "\n", "**Output:** A tuple of two numbers $(x_1', x_2')$ - your estimates of the amplitudes of the state $|1\\rangle$ and $|2\\rangle$, respectively.\n", "The absolute errors $|x_1 - x_1'|$ and $|x_2 - x_2'|$ should be less than or equal to 0.001.\n", "\n", "The test will call your code exactly once, with the fixed state parameter that will not change if you run the cell several times." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%kata T2_LearnBasisStateAmplitudes\n", "\n", "operation LearnBasisStateAmplitudes (qs : Qubit[]) : (Double, Double) {\n", " // ...\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Configure DumpMachine output\n", "\n", "Sometimes you want to focus on certain properties of the quantum state, such as the relative phases of individual basis states, or on certain parts of the state, such as the basis states with larger amplitudes associated with them. \n", "[%config](https://docs.microsoft.com/qsharp/api/iqsharp-magic/config) magic command allows you to tweak the format of `DumpMachine` output (available in Q# Jupyter Notebooks only). \n", "Here are some useful options that help you extract the right information:\n", "\n", "* `dump.basisStateLabelingConvention` sets the way the basis states are labeled in the output. Here is how the basis state $|10\\rangle$ will look like with different settings:\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
dump.basisStateLabelingConvention valueDumpMachine output (first two columns)
\"LittleEndian\" (default)
\"BigEndian\"
\"Bitstring\"
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* `dump.truncateSmallAmplitudes`, when set to `true`, removes basis states with measurement probabilities less than a certain threshold from the output. \n", "By default the measurement probability has to be less than $10^{-10}$ for the state to be truncated; you can change that threshold using the `dump.truncationThreshold` setting.\n", "* `dump.measurementDisplayStyle` and `dump.phaseDisplayStyle` set the style of display for \"Measurement Probability\" and \"Phase\" columns; you can choose between bar/arrow, number, both, or none for each of the columns. \n", "If you choose to display the numeric value of measurement probability, you can configure its probability using `dump.measurementDisplayPrecision`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise 3: High-probability basis states\n", "\n", "**Input:** $N$ qubits in an unknown state $|\\psi\\rangle = \\sum_{k = 0}^{2^N-1} x_k |k\\rangle$. The amplitudes $x_k$ will be real and non-negative.\n", "\n", "**Output:** An array of integers which represent the basis states which have amplitudes bigger than 0.01 (in little endian encoding). The integers can be returned in any order.\n", "\n", "The test will call your code exactly once, with the fixed state parameter that will not change if you run the cell several times." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%kata T3_HighProbabilityBasisStates\n", "\n", "operation HighProbabilityBasisStates (qs : Qubit[]) : Int[] {\n", " // ...\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Display the matrix implemented by the operation\n", "\n", "Let's consider a more complex scenario: you've written a Q# operation and want to check that it implements exactly the matrix you're looking for. \n", "(An examples of such cases could be quantum oracle implementation, unitary gate synthesis, or more uncommon scenarios such as the [UnitaryPatterns kata](../../UnitaryPatterns/UnitaryPatterns.ipynb)).\n", "\n", "You could do this semi-manually, by applying the operation to each basis state in turn and checking the amplitudes of the resulting states. \n", "However, [DumpOperation](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.diagnostics.dumpoperation) library operation offers you a shorter and more elegant way to do this.\n", "\n", "> `DumpOperation` is available both in standalone Q# applications as well, but the outputs it produces differs slightly: in plain text mode it is printed with the real and the imaginary components separated into two matrices.\n", "\n", "Similarly to `DumpMachine`, the output of `DumpOperation` is accurate up to a global phase.\n", "\n", "### Demo: Using DumpOperation\n", "\n", "`DumpOperation` requires an operation that acts on an array of qubits. \n", "If your operation acts on a single qubit, such as most intrinsic gates, or on a mix of individual qubits and qubit arrays, you'll need to write a wrapper for it that takes a single array of qubits as an argument and applies the operation to the right qubits of that array." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "open Microsoft.Quantum.Diagnostics;\n", "\n", "operation DumpOperationDemo () : Unit {\n", " DumpOperation(2, ApplyToFirstTwoQubitsCA(CNOT, _));\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%simulate DumpOperationDemo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> Note that the matrix of the CNOT gate produced by `DumpOperation` in this demo might differ from the one you are used to seeing in quantum computing resources, such as the [multi-qubit gates tutorial](../MultiQubitGates/MultiQubitGates.ipynb#CNOT-Gate):\n", "> $$\\begin{bmatrix} 1 & 0 & 0 & 0 \\\\ 0 & 1 & 0 & 0 \\\\ 0 & 0 & 0 & 1 \\\\ 0 & 0 & 1 & 0 \\end{bmatrix}$$\n", ">\n", "> Similarly to `DumpMachine`, the way `DumpOperation` evaluates the matrix depends on the conventions used by the simulator. \n", "> Q# full state simulator tends to use little-endian encoding for converting basis states to integers, and `DumpOperation` uses the same convention for converting basis states to the indices of matrix elements.\n", "> Thus, the second column of the CNOT matrix corresponds to the input state $|1\\rangle_{LE} = |10\\rangle$, which the CNOT gate converts to $|11\\rangle = |3\\rangle_{LE}$, meaning that the last element is going to be $1$.\n", ">\n", "> A lot of resources on quantum computing use big-endian encoding, so in them the second column of the CNOT matrix corresponds to the input state $|1\\rangle_{BE} = |01\\rangle$, which should be left unchanged by the CNOT gate." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise 4: Read the operation matrix\n", "\n", "In this task you will be interpreting the matrix of an operation `MysteryOperation1` that implements a unitary operation $U$, defined in the `Quantum.Kata.VisualizationTools` namespace. \n", "This operation will not be passed as an input to your task; you can access it by creating a separate code cell with a new operation (or modifying `DumpOperationDemo`), and using `%simulate` for that operation.\n", "\n", "**Input:** None.\n", "\n", "**Output:** A single floating-point number: the amplitude of the $|10\\rangle$ basis state in the state $U|01\\rangle$ (i.e., the element of the matrix implemented by $U$ which describes the transformation $|01\\rangle \\rightarrow |10\\rangle$). The result must be within 0.001 from the actual value.\n", "\n", "
\n", "
\n", " Need a hint? Click here\n", " There are two ways to do this task. You can use DumpOperation directly, or (since you're looking for just one coefficient, not all of them) you can allocate 2 qubits, prepare them in the right basis state, apply the MysteryOperation1 and use DumpMachine to see the amplitudes of the resulting superposition state.\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%kata T4_ReadOperationMatrix\n", "\n", "operation ReadMysteryOperationMatrix () : Double {\n", " // ...\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Display the circuit performed by the operation\n", "\n", "[Quantum circuits](https://en.wikipedia.org/wiki/Quantum_circuit) are a very common way of visualizing quantum programs; you'll see them in a lot of tutorials, papers and books on quantum computing.\n", "Quantum circuits are a less powerful way of expressing the quantum computation compared to a quantum program. \n", "They don't offer a good way to show the values of classical variables and their evolution, the decisions made based on the classical parameters or the measurement results, or even flow control structures such as loops or conditional statements.\n", "At the same time, they can be convenient to get a quick idea of what the program did, or to compare your implementation to the one offered in a book.\n", "\n", "[`%trace` magic command](https://docs.microsoft.com/qsharp/api/iqsharp-magic/trace) (available only in Q# Jupyter Notebooks) offers a way to trace one run of the Q# program and to build a circuit based on that execution.\n", "Note that these circuits include only the quantum gates executed by the program. \n", "They might differ in different runs of the same program, if that program takes parameters, has conditional branching or other behaviors that can change the sequence of gates applied by the program.\n", "\n", "You can pass parameters to the operation traced with `%trace` same as for `%simulate`, by adding `=` for each parameter after the operation name.\n", "\n", "\n", "### Demo: Using %trace\n", "\n", "Let's take a look at the circuit produced by the `MultiQubitDumpMachineDemo` operation defined earlier.\n", "\n", "> If the cell below gives you an \"Invalid operation name\" error, return to the demo [DumpMachine for multi-qubit systems](#Demo:-DumpMachine-for-multi-qubit-systems) and run it to define the operation.\n", "\n", "Try clicking on the gates that show a magnifying glass when the cursor hovers over them to see their internal implementation." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "%trace MysteryOperation2 N=3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise 5: Read the trace of the operation\n", "\n", "In this task you will be interpreting the trace of an operation `MysteryOperation2` that implements a certain quantum algorithm. \n", "This operation will not be passed as an input to your task; you can access it by creating a separate code cell with a new operation or using `%trace` for that operation directly.\n", "\n", "`MysteryOperation2` takes a single integer named $N$ as an input.\n", "\n", "**Input:** None.\n", "\n", "**Output:** A tuple of 3 integers that describe the behavior of `MysteryOperation2` for its input $N = 3$:\n", "\n", "1. The number of qubits used by the operation (the \"width\" of the circuit).\n", "2. The number of Hadamard gates used by it (including adjoint Hadamard gates, which are the same as Hadamard gates).\n", "3. The number of CNOT gates used by it." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%kata T5_ReadOperationTrace\n", "\n", "operation ReadMysteryOperationTrace () : (Int, Int, Int) {\n", " // ...\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Run through the program execution gate by gate\n", "\n", "Finally, [`%debug` magic command](https://docs.microsoft.com/qsharp/api/iqsharp-magic/debug) (available only in Q# Jupyter Notebooks) allows you to combine tracing the program execution (as a circuit) and observing the program state at the same time.\n", "\n", "### Demo: Using %debug\n", "\n", "> You cannot switch to executing a different notebook cell in the middle of the debug session.\n", "If you need to stop before it's complete, you can use `Kernel -> Interrupt` menu item." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "timeout" ] }, "outputs": [], "source": [ "%debug MysteryOperation2 N=2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion\n", "\n", "We hope you've learned a lot from this tutorial.\n", "With these tools you're well equipped to continue your quantum computing journey!" ] } ], "metadata": { "kernelspec": { "display_name": "Q#", "language": "qsharp", "name": "iqsharp" }, "language_info": { "file_extension": ".qs", "mimetype": "text/x-qsharp", "name": "qsharp", "version": "0.14" } }, "nbformat": 4, "nbformat_minor": 2 }