{ "cells": [ { "cell_type": "markdown", "id": "39e84d80-670c-45f1-be88-0bd765523bfa", "metadata": { "tags": [] }, "source": [ "# Visualize Eka objects\n", "```{note}\n", "The widgets in this notebook are interactive!\n", "```\n", "\n", "This notebook demonstrates the functions of the `visualizer` module for creating stabilizer plots. More concretely, this notebook contains:\n", "* plotting the stabilizers of a `Block`\n", "* plotting the logical operators of a `Block`\n", "* plotting arbitrary pauli strings\n", "* plotting the pauli charges of a `Block`\n", "* plotting multiple `Block`s on a lattice" ] }, { "cell_type": "markdown", "id": "dd6ccbae", "metadata": {}, "source": [ "First, we import all necessary modules:" ] }, { "cell_type": "code", "execution_count": 1, "id": "dc9692a7", "metadata": {}, "outputs": [], "source": [ "import loom.visualizer as vis\n", "from loom.eka import Lattice, PauliOperator, Stabilizer, Block\n", "import plotly.io as pio\n", "\n", "pio.renderers.default = \"notebook\"" ] }, { "cell_type": "markdown", "id": "aba8c4ce-279a-4b42-bdcd-d1d94e5796d5", "metadata": {}, "source": [ "## Rotated surface code\n", "Let's create a 3x3 rotated surface code block on a square lattice. We make the square lattice a 5x5 lattice and place the rotated surface code block in the upper left corner.\n", "\n", "The (0,0) position is at the top left. The first coordinate is the x-axis with the positive x-axis going to the right. The second coordinate is the y-axis with the positive y-axis going to the bottom. " ] }, { "cell_type": "code", "execution_count": null, "id": "7d4d0e9a", "metadata": {}, "outputs": [ { "data": { "text/html": [ " \n", " \n", " " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "dx = 3\n", "dz = 3\n", "lattice = Lattice.square_2d((dx, dz))\n", "\n", "\n", "# Let's define a rotated surface code on a 3x3 lattice at the origin (0, 0).\n", "def build_3x3_rotated_surface_code() -> Block:\n", " rsc_stabilizers = (\n", " Stabilizer(\n", " \"ZZZZ\",\n", " ((1, 0, 0), (0, 0, 0), (1, 1, 0), (0, 1, 0)),\n", " ancilla_qubits=((1, 1, 1),),\n", " ),\n", " Stabilizer(\n", " \"ZZZZ\",\n", " ((2, 1, 0), (1, 1, 0), (2, 2, 0), (1, 2, 0)),\n", " ancilla_qubits=((2, 2, 1),),\n", " ),\n", " Stabilizer(\n", " \"XXXX\",\n", " ((1, 1, 0), (1, 2, 0), (0, 1, 0), (0, 2, 0)),\n", " ancilla_qubits=((1, 2, 1),),\n", " ),\n", " Stabilizer(\n", " \"XXXX\",\n", " ((2, 0, 0), (2, 1, 0), (1, 0, 0), (1, 1, 0)),\n", " ancilla_qubits=((2, 1, 1),),\n", " ),\n", " Stabilizer(\n", " \"XX\",\n", " ((0, 0, 0), (0, 1, 0)),\n", " ancilla_qubits=((0, 1, 1),),\n", " ),\n", " Stabilizer(\n", " \"XX\",\n", " ((2, 1, 0), (2, 2, 0)),\n", " ancilla_qubits=((3, 2, 1),),\n", " ),\n", " Stabilizer(\n", " \"ZZ\",\n", " ((2, 0, 0), (1, 0, 0)),\n", " ancilla_qubits=((2, 0, 1),),\n", " ),\n", " Stabilizer(\n", " \"ZZ\",\n", " ((1, 2, 0), (0, 2, 0)),\n", " ancilla_qubits=((1, 3, 1),),\n", " ),\n", " )\n", " rot_surf_code_1 = Block(\n", " unique_label=\"q1\",\n", " stabilizers=rsc_stabilizers,\n", " logical_x_operators=(PauliOperator(\"XXX\", ((0, 0, 0), (1, 0, 0), (2, 0, 0))),),\n", " logical_z_operators=(PauliOperator(\"ZZZ\", ((0, 0, 0), (0, 1, 0), (0, 2, 0))),),\n", " )\n", "\n", " return rot_surf_code_1\n", "\n", "\n", "rot_surf_code_1 = build_3x3_rotated_surface_code()\n", "\n", "stab_plot = vis.StabilizerPlot(\n", " lattice,\n", " title=f\"Rotated Surface Code, {dx} x {dz}\",\n", ")\n", "stab_plot.add_dqubit_traces()\n", "stab_plot.plot_blocks([rot_surf_code_1])\n", "stab_plot._fig.update_layout(\n", " margin=dict(t=60, l=30, b=30), width=680\n", ") # Sphinx formatting\n", "stab_plot.show()" ] }, { "cell_type": "markdown", "id": "0e126de4", "metadata": {}, "source": [ "The surface code block does not need to be a square. Let's repeat the process of creation and plotting for a bigger code:" ] }, { "cell_type": "code", "execution_count": null, "id": "dd61ea6e", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from itertools import product\n", "\n", "dx = 9\n", "dz = 5\n", "lattice = Lattice.square_2d((dx + 2, dz + 2))\n", "\n", "\n", "def create_9x5_rotated_surface_code() -> Block:\n", " xxxx_stabs = tuple(\n", " Stabilizer(\"XXXX\", ((i, j, 0), (i + 1, j, 0), (i + 1, j + 1, 0), (i, j + 1, 0)))\n", " for i, j in product(range(9 - 1), range(5 - 1))\n", " if (i + j) % 2 == 1\n", " )\n", " zzzz_stabs = tuple(\n", " Stabilizer(\"ZZZZ\", ((i, j, 0), (i + 1, j, 0), (i + 1, j + 1, 0), (i, j + 1, 0)))\n", " for i, j in product(range(9 - 1), range(5 - 1))\n", " if (i + j) % 2 == 0\n", " )\n", " xx_stabs = tuple(\n", " Stabilizer(\"XX\", ((i, j, 0), (i, j + 1, 0)))\n", " for i, j in ((0, 0), (0, 2), (8, 1), (8, 3))\n", " )\n", " zz_stabs = tuple(\n", " Stabilizer(\"ZZ\", ((i, j, 0), (i + 1, j, 0)))\n", " for i, j in ((1, 0), (3, 0), (5, 0), (7, 0), (0, 4), (2, 4), (4, 4), (6, 4))\n", " )\n", " logical_x = PauliOperator(\"XXXXXXXXX\", tuple((i, 0, 0) for i in range(9)))\n", " logical_z = PauliOperator(\"ZZZZZ\", tuple((0, i, 0) for i in range(5)))\n", " return Block(\n", " unique_label=\"q1\",\n", " stabilizers=xxxx_stabs + zzzz_stabs + xx_stabs + zz_stabs,\n", " logical_x_operators=(logical_x,),\n", " logical_z_operators=(logical_z,),\n", " ).shift((1, 1))\n", "\n", "\n", "big_rsc_block = create_9x5_rotated_surface_code()\n", "stab_plot = vis.StabilizerPlot(\n", " lattice,\n", " title=f\"Rotated Surface Code, {dx} x {dz}\",\n", " height=800,\n", " width=1200,\n", ")\n", "stab_plot.add_dqubit_traces()\n", "stab_plot.plot_blocks([big_rsc_block])\n", "stab_plot._fig.update_layout(\n", " margin=dict(t=60, l=30, b=30), xaxis=dict(range=[1, 7]), width=680\n", ") # Sphinx formatting\n", "stab_plot.show()" ] }, { "cell_type": "markdown", "id": "0d2557ec", "metadata": {}, "source": [ "## Plot arbitrary Pauli strings\n", "One can plot an arbitrary Pauli string by defining a corresponding `PauliOperator` and then plotting it using `plot_pauli_string()`." ] }, { "cell_type": "code", "execution_count": null, "id": "299d8174", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pauli_string = PauliOperator(\n", " pauli=\"XYZZYX\",\n", " data_qubits=[(3, 1, 0), (3, 2, 0), (4, 3, 0), (6, 3, 0), (6, 4, 0), (5, 5, 0)],\n", ")\n", "\n", "stab_plot = vis.StabilizerPlot(\n", " lattice,\n", " title=f\"Rotated Surface Code, {dx} x {dz}\",\n", " height=800,\n", " width=1200,\n", ")\n", "stab_plot.add_dqubit_traces()\n", "stab_plot.plot_blocks(big_rsc_block, plot_logical_operators=False)\n", "stab_plot.plot_pauli_string(pauli_string)\n", "stab_plot._fig.update_layout(\n", " margin=dict(t=60, l=30, b=30), xaxis=dict(range=[1, 6]), width=680\n", ") # Sphinx formatting\n", "stab_plot.show()" ] }, { "cell_type": "markdown", "id": "7ebc4926", "metadata": {}, "source": [ "## Plot Pauli charges\n", "There is also a fast way for plotting Pauli charges of a block.\n", "\n", "**What are Pauli charges?**\n", "\n", "The Pauli charges are calculated from the stabilizers of a `Block`. For every data qubit, one counts how often the data qubit is included in stabilizers in the X, Y, and Z basis respectively. If it is included in an odd number of X, Y, or Z stabilizers, the data qubit has a Pauli charge of X, Y, or Z respectively. We only report a single Pauli charge where multiple charges are combined into one charge according to the product of Pauli matrices. E.g. if a data qubit has a Pauli charge of both X and Z, the combined Pauli charge is Y since Y=iXZ." ] }, { "cell_type": "code", "execution_count": null, "id": "53b8e3d5", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "stab_plot = vis.StabilizerPlot(\n", " lattice,\n", " title=f\"Rotated Surface Code, {dx} x {dz}\",\n", " height=800,\n", " width=1200,\n", ")\n", "stab_plot.add_dqubit_traces()\n", "stab_plot.plot_blocks(\n", " big_rsc_block, plot_logical_operators=False, plot_pauli_charges=True\n", ")\n", "stab_plot._fig.update_layout(\n", " margin=dict(t=60, l=30, b=30), xaxis=dict(range=[1, 6]), width=680\n", ")\n", "stab_plot.show()" ] }, { "cell_type": "markdown", "id": "867ce296", "metadata": {}, "source": [ "## Plot multiple qubits on the same lattice\n", "Of course one can also plot multiple blocks on the same lattice. Note how the arguments `x_boundary` and `weight_2_stab_is_first_row` are used here to control the boundaries and the position of weight-2 stabilizers of the blocks." ] }, { "cell_type": "code", "execution_count": null, "id": "ced8a843", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lattice = Lattice.square_2d((7, 7))\n", "\n", "q1 = rot_surf_code_1\n", "q2 = rot_surf_code_1.shift((0, 4), new_label=\"q2\")\n", "q3 = rot_surf_code_1.shift((4, 0), new_label=\"q3\")\n", "\n", "stab_plot = vis.StabilizerPlot(\n", " lattice,\n", " height=800,\n", " width=800,\n", ")\n", "stab_plot.add_dqubit_traces()\n", "stab_plot.plot_blocks([q1, q2, q3], plot_logical_operators=False)\n", "stab_plot._fig.update_layout(\n", " margin=dict(t=60, l=30, b=30), xaxis=dict(range=[0, 5]), width=680\n", ") # Sphinx formatting\n", "stab_plot.show()" ] }, { "cell_type": "markdown", "id": "7bc7f069", "metadata": {}, "source": [ "## Plot only a subset of stabilizers\n", "\n", "Using the function `add_stabilizers`, one can plot an arbitrary set of stabilizers. This could be useful for blocks with overlapping stabilizers where one only wants to plot some of them for better visibility. \n", "\n", "For example, one could only plot the Z type stabilizers of a rotated surface code block:" ] }, { "cell_type": "code", "execution_count": null, "id": "84311c1f", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "dx = 9\n", "dz = 5\n", "lattice = Lattice.square_2d((dx + 2, dz + 2))\n", "Z_stabs = [stab for stab in big_rsc_block.stabilizers if set(stab.pauli) == {\"Z\"}]\n", "\n", "stab_plot = vis.StabilizerPlot(\n", " lattice,\n", " title=f\"Rotated Surface Code, {dx} x {dz}\",\n", " height=800,\n", " width=1200,\n", ")\n", "stab_plot.add_dqubit_traces()\n", "stab_plot.add_stabilizers(Z_stabs)\n", "stab_plot._fig.update_layout(\n", " margin=dict(t=60, l=30, b=30), xaxis=dict(range=[1, 4]), width=680\n", ") # Sphinx formatting\n", "stab_plot.show()" ] }, { "cell_type": "markdown", "id": "f08641a9", "metadata": {}, "source": [ "## Creating a plot from scratch\n", "\n", "The stabilizer plot does not need to be based on blocks. One can also manually define stabilizers and plot those." ] }, { "cell_type": "code", "execution_count": null, "id": "08c64a7f", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "lattice = Lattice.square_2d((4, 4))\n", "stabs = [\n", " Stabilizer(\"XXX\", ((0, 0, 0), (0, 1, 0), (1, 0, 0))),\n", " Stabilizer(\"YZZY\", ((2, 0, 0), (2, 1, 0), (1, 2, 0), (0, 2, 0))),\n", " Stabilizer(\"XXX\", ((0, 2, 0), (0, 3, 0), (1, 3, 0))),\n", " Stabilizer(\"YZZY\", ((3, 1, 0), (3, 2, 0), (3, 3, 0), (1, 3, 0))),\n", "]\n", "stab_plot = vis.StabilizerPlot(\n", " lattice,\n", ")\n", "stab_plot.add_dqubit_traces()\n", "stab_plot.add_stabilizers(stabs)\n", "stab_plot._fig.update_layout(\n", " margin=dict(t=60, l=30, b=30), xaxis=dict(range=[0, 3]), width=680\n", ") # Sphinx formatting\n", "stab_plot.show()" ] }, { "cell_type": "markdown", "id": "c412e87e", "metadata": {}, "source": [ "## Non-rotated surface code\n", "Another common code is the non-rotated surface code. In this example, observe the parameter `dqb_plot_indices=False` which hides the labels of data qubits in the plot:" ] }, { "cell_type": "code", "execution_count": null, "id": "4748d14c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "d = 5\n", "custom_lattice = Lattice(\n", " lattice_vectors=((1, 0), (0, 1)),\n", " basis_vectors=((0, 0),),\n", " size=(2 * d - 1, 2 * d - 1),\n", ")\n", "\n", "\n", "def create_5x5_non_rotated_surface_code() -> Block:\n", " x_stabs = []\n", " z_stabs = []\n", " for base_point in product(range(0, 2 * d, 2), repeat=2):\n", " x_stab_indices = (\n", " (base_point[0], base_point[1]),\n", " (base_point[0] + 1, base_point[1] + 1),\n", " (base_point[0] + 1, base_point[1] - 1),\n", " (base_point[0] + 2, base_point[1]),\n", " )\n", " valid_x_stab_indices = [\n", " coord\n", " for coord in x_stab_indices\n", " if coord[0] >= 0\n", " and coord[1] >= 0\n", " and coord[0] < 2 * d - 1\n", " and coord[1] < 2 * d - 1\n", " ]\n", " if len(valid_x_stab_indices) > 2:\n", " x_stab = Stabilizer(\n", " pauli=\"X\" * len(valid_x_stab_indices),\n", " data_qubits=valid_x_stab_indices,\n", " )\n", " x_stabs.append(x_stab)\n", "\n", " z_stab_indices = (\n", " (base_point[0], base_point[1]),\n", " (base_point[0] + 1, base_point[1] + 1),\n", " (base_point[0] - 1, base_point[1] + 1),\n", " (base_point[0], base_point[1] + 2),\n", " )\n", " valid_z_stab_indices = [\n", " coord\n", " for coord in z_stab_indices\n", " if coord[0] >= 0\n", " and coord[1] >= 0\n", " and coord[0] < 2 * d - 1\n", " and coord[1] < 2 * d - 1\n", " ]\n", "\n", " if len(valid_z_stab_indices) > 2:\n", " z_stab = Stabilizer(\n", " pauli=\"Z\" * len(valid_z_stab_indices),\n", " data_qubits=valid_z_stab_indices,\n", " )\n", " z_stabs.append(z_stab)\n", "\n", " logical_x = PauliOperator(\n", " \"XXXXX\", data_qubits=tuple((0, i) for i in range(0, 2 * d, 2))\n", " )\n", " logical_z = PauliOperator(\n", " \"ZZZZZ\", data_qubits=tuple((i, 0) for i in range(0, 2 * d, 2))\n", " )\n", "\n", " surface_code_block = Block(\n", " unique_label=\"q1\",\n", " stabilizers=x_stabs + z_stabs,\n", " logical_x_operators=(logical_x,),\n", " logical_z_operators=(logical_z,),\n", " )\n", " return surface_code_block\n", "\n", "\n", "surface_code_block = create_5x5_non_rotated_surface_code()\n", "fig = vis.StabilizerPlot(\n", " lattice=custom_lattice,\n", " title=f\"Non-Rotated Surface Code, d={d}\",\n", " dqb_plot_indices=False, # Hide the numbers of data qubits in the plot\n", " opacity_stabs=0.8,\n", " height=800,\n", " width=1200,\n", ")\n", "# We only plot the right qubits (pair indices)\n", "fig.add_dqubit_traces(\n", " {i: True if i % 2 == 0 else False for i in range((2 * d - 1) ** 2)}\n", ")\n", "fig.plot_blocks([surface_code_block])\n", "fig._fig.update_layout(\n", " margin=dict(t=60, l=30, b=30), xaxis=dict(range=[0, 5]), width=680\n", ") # Sphinx formatting\n", "fig.show()" ] }, { "cell_type": "markdown", "id": "187acad0", "metadata": {}, "source": [] } ], "metadata": { "kernelspec": { "language": "python" }, "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.12.9" } }, "nbformat": 4, "nbformat_minor": 5 }