From 38a63466f05d790790bf317dc41a91eb613df552 Mon Sep 17 00:00:00 2001 From: Mark Shui Hu Date: Wed, 4 Nov 2020 22:52:51 +0100 Subject: [PATCH] Updated docs. PyPI release build. (#30) * docs checked * Docs finished. All passing. PyPI release build * bump version * add documenation url to pypi --- .gitignore | 1 + README.md | 45 ++--- docs/source/codes/elements.rst | 2 + docs/source/codes/planar.rst | 4 +- docs/source/codes/template.rst | 4 +- docs/source/codes/toric.rst | 4 +- docs/source/conf.py | 9 +- docs/source/decoders/mwpm.rst | 1 + docs/source/decoders/template.rst | 7 +- docs/source/decoders/ufns.rst | 2 + docs/source/decoders/unionfind.rst | 10 +- docs/source/errors/template.rst | 1 + examples.ipynb | 122 +++++++------ opensurfacesim/__init__.py | 2 +- opensurfacesim/__main__.py | 8 +- opensurfacesim/codes/_template/plot.py | 41 +++-- opensurfacesim/codes/_template/sim.py | 165 ++++++++++-------- opensurfacesim/codes/elements.py | 46 ++--- opensurfacesim/codes/planar/plot.py | 22 ++- opensurfacesim/codes/planar/sim.py | 4 +- opensurfacesim/codes/toric/plot.py | 22 ++- opensurfacesim/codes/toric/sim.py | 5 +- opensurfacesim/decoders/_template.py | 41 +++-- opensurfacesim/decoders/mwpm/__init__.py | 12 +- opensurfacesim/decoders/mwpm/blossom5/LICENSE | 52 +++--- opensurfacesim/decoders/mwpm/plot.py | 24 +-- opensurfacesim/decoders/mwpm/sim.py | 12 +- opensurfacesim/decoders/ufns/elements.py | 29 ++- opensurfacesim/decoders/ufns/plot.py | 4 +- opensurfacesim/decoders/ufns/sim.py | 26 ++- opensurfacesim/decoders/unionfind/__init__.py | 2 +- opensurfacesim/decoders/unionfind/plot.py | 48 +++-- opensurfacesim/decoders/unionfind/sim.py | 20 +-- opensurfacesim/errors/_template.py | 141 ++++++++++++--- opensurfacesim/errors/erasure.py | 32 ++-- opensurfacesim/errors/pauli.py | 22 +-- opensurfacesim/main.py | 41 +++-- opensurfacesim/plot.py | 2 + opensurfacesim/threshold.py | 45 ++++- setup.py | 16 +- 40 files changed, 708 insertions(+), 388 deletions(-) diff --git a/.gitignore b/.gitignore index da93a4b0..95cad576 100644 --- a/.gitignore +++ b/.gitignore @@ -84,6 +84,7 @@ instance/ # Sphinx documentation docs/build/ +docs/source/_build # PyBuilder .pybuilder/ diff --git a/README.md b/README.md index cfb3fbcd..7b06b099 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -# Introduction +# opensurfacesim + ![Build](https://github.com/watermarkhu/opensurfacesim/workflows/Build/badge.svg) [![codecov](https://codecov.io/gh/watermarkhu/OpenSurfaceSim/branch/master/graph/badge.svg?token=CWLVPDFF2L)](https://codecov.io/gh/watermarkhu/OpenSurfaceSim) [![Documentation Status](https://readthedocs.org/projects/opensurfacesim/badge/?version=latest)](https://opensurfacesim.readthedocs.io/en/latest/?badge=latest) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/watermarkhu/opensurfacesim/master?filepath=examples.ipynb) [![Unitary Fund](https://img.shields.io/badge/Supported%20By-UNITARY%20FUND-brightgreen.svg?style=flat-the-badge)](http://unitary.fund) Opensurfacesim is a simulation package for the surface code, and is designed to modularize 3 aspects of a surface code simulation. @@ -11,13 +13,15 @@ Opensurfacesim is a simulation package for the surface code, and is designed to 2. The error model 3. The used decoder -New types of surface codes, error modules and decoders can be added to opensurfacesim by using the included templates for each of the three core modules. +New types of surface codes, error modules and decoders can be added to opensurfacesim by using the included templates for each of the three core module categories. The current included decoders are: * The *Mininum-Weight Perfect Matching* (`mwpm`) decoder. -* Delfosse's and Nickerson's [Union-Find](https://arxiv.org/pdf/1709.06218.pdf) (`unionfind`) decoder, which has *almost-linear* complexity. -* Our modification to the Union-Find decoder; the **Union-Find Node-Suspension** (`ufns`) decoder, which improves the threshold of the Union-Find decoder to near MWPM performance, while retaining low complexity. +* [Delfosse's and Nickerson's](https://arxiv.org/pdf/1709.06218.pdf) *Union-Find* (`unionfind`) decoder, which has *almost-linear* worst-case time complexity. +* Our modification to the Union-Find decoder; the *Union-Find Node-Suspension* (`ufns`) decoder, which improves the threshold of the Union-Find decoder to near MWPM performance, while retaining quasi-linear worst-case time complexity. + +The compatibility of these decoders with the included surface codes are listed below. | Decoders | `toric` code | `planar` code | |-----------|--------------|---------------| @@ -27,7 +31,7 @@ The current included decoders are: # Installation -~~All required packages can be installed through:~~ +All required packages can be installed through: ```bash pip install opensurfacesim @@ -36,7 +40,7 @@ pip install opensurfacesim ## Requirements * Python 3.7+ -* Tkinter for interactive plotting. Your Python distribution may or may not bundled Tkinter already. Check out [this guide from Realpython.com](https://realpython.com/python-gui-tkinter/) to install Tkinter if you encounter any problems. +* Tkinter for interactive plotting. Your Python distribution may or may not bundled Tkinter already. Check out this [guide](https://realpython.com/python-gui-tkinter/) from realpython.com to install Tkinter if you encounter any problems. * Matplotlib 3.4+ for plotting on a 3D lattice (Refers to a future release of matplotlib, see [pull request](https://github.com/matplotlib/matplotlib/pull/18816)) ### MWPM decoder @@ -50,7 +54,7 @@ The MWPM decoder utilizes `networkx` for finding the minimal weights in a fully # Usage -To simulate the toric code and simulate with bit-flip error for 10 iterations and decode with the MWPM decoder: +To simulate the toric code and simulate with bitflip error for 10 iterations and decode with the MWPM decoder: ```python >>> from opensurfacesim.main import initialize, run @@ -72,23 +76,24 @@ Benchmarking of decoders can be enabled by attaching a *benchmarker* object to t 'std': 0.002170364089572033}}}} ``` -To enable interactive plotting, the user must enabled the Tk backend (see below). The figures in opensurfacesim allows for step-by-step visualization of the surface code simulation (and if supported the decoding process). Each figure logs its history such that the user can move backwards in time to view past states of the surface (and decoder). Press `h` when the figure is open for more information. +The figures in opensurfacesim allows for step-by-step visualization of the surface code simulation (and if supported the decoding process). Each figure logs its history such that the user can move backwards in time to view past states of the surface (and decoder). Press `h` when the figure is open for more information. ```python ->>> import matplotlib ->>> matplotlib.use("TkAgg") >>> from opensurfacesim.main import initialize, run ->>> code, decoder = initialize((6,6), "toric", "mwpm", enabled_errors=["pauli"], plotting=True) ->>> run(code, decoder, error_rates = {"p_bitflip": 0.1, "p_phaseflip": 0.1}) -Drawing 0/4: Initial -Drawing 1/4: Errors applied -Drawing 2/4: Ancilla-qubits measured -Drawing 3/4: Matchings found -Drawing 4/4: Decoded. -{'no_error': 8} +>>> code, decoder = initialize((6,6), "toric", "mwpm", enabled_errors=["pauli"], plotting=True, initial_states=(0,0)) +>>> run(code, decoder, error_rates = {"p_bitflip": 0.1, "p_phaseflip": 0.1}, decode_initial=False) +``` + +![Interactive plotting on a 6x6 toric code.](https://raw.githubusercontent.com/watermarkhu/OpenSurfaceSim/master/images/toric-2d.gif "Iteractive plotting on a 2d axis") + +Plotting will be performed on a 3D axis if faulty measurements are enabled. + +```python +>>> code, decoder = initialize((3,3), "toric", "mwpm", enabled_errors=["pauli"], faulty_measurements=True, plotting=True, initial_states=(0,0)) +>>> run(code, decoder, error_rates = {"p_bitflip": 0.05, "p_bitflip_plaq": 0.05}, decode_initial=False) ``` -![Interactive plotting on a 6x6 toric code.](https://raw.githubusercontent.com/watermarkhu/OpenSurfaceSim/master/images/toric.gif "Iteractive plotting") +![Interactive plotting on a toric code with faulty measurements.](https://raw.githubusercontent.com/watermarkhu/OpenSurfaceSim/master/images/toric-2d.gif "Iteractive plotting on a 3d axis") Simulations can also be initiated from the command line @@ -105,4 +110,4 @@ usage: opensurfacesim ... ``` -*This project is proudly funded by the [Unitary Fund](https://unitary.fund/).* \ No newline at end of file +*This project is proudly funded by the [Unitary Fund](https://unitary.fund/).* diff --git a/docs/source/codes/elements.rst b/docs/source/codes/elements.rst index a96c68ed..f8132ee7 100644 --- a/docs/source/codes/elements.rst +++ b/docs/source/codes/elements.rst @@ -2,5 +2,7 @@ Code elements ------------- .. automodule:: opensurfacesim.codes.elements + :member-order: bysource + :inherited-members: :members: diff --git a/docs/source/codes/planar.rst b/docs/source/codes/planar.rst index 1d81b3e4..88d59c8d 100644 --- a/docs/source/codes/planar.rst +++ b/docs/source/codes/planar.rst @@ -20,4 +20,6 @@ Plotting .. autoclass:: opensurfacesim.codes.planar.plot.PerfectMeasurements :member-order: bysource :inherited-members: - :members: \ No newline at end of file + :members: + +.. autoclass:: opensurfacesim.codes.planar.plot.FaultyMeasurements \ No newline at end of file diff --git a/docs/source/codes/template.rst b/docs/source/codes/template.rst index e0571a11..0e1b23f3 100644 --- a/docs/source/codes/template.rst +++ b/docs/source/codes/template.rst @@ -20,4 +20,6 @@ Plotting .. autoclass:: opensurfacesim.codes._template.plot.PerfectMeasurements :member-order: bysource :inherited-members: - :members: \ No newline at end of file + :members: + +.. autoclass:: opensurfacesim.codes._template.plot.FaultyMeasurements \ No newline at end of file diff --git a/docs/source/codes/toric.rst b/docs/source/codes/toric.rst index 0663e6f8..ad07838c 100644 --- a/docs/source/codes/toric.rst +++ b/docs/source/codes/toric.rst @@ -20,4 +20,6 @@ Plotting .. autoclass:: opensurfacesim.codes.toric.plot.PerfectMeasurements :member-order: bysource :inherited-members: - :members: \ No newline at end of file + :members: + +.. autoclass:: opensurfacesim.codes.toric.plot.FaultyMeasurements \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 68a57a93..20c3afd0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -31,15 +31,16 @@ "sphinx.ext.autodoc", "sphinx_autodoc_typehints", "sphinx.ext.todo", - "sphinx.ext.githubpages", "sphinx.ext.intersphinx", "m2r2", "sphinx_rtd_theme", ] -intersphinx_mapping = {"matplotlib": ("https://matplotlib.org/", None)} -intersphinx_mapping = {"networkx": ("https://networkx.github.io/documentation/stable/", None)} - +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), + "matplotlib": ("https://matplotlib.org/", None), + "networkx": ("https://networkx.github.io/documentation/stable/", None), +} # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] diff --git a/docs/source/decoders/mwpm.rst b/docs/source/decoders/mwpm.rst index ddd9b399..8a604731 100644 --- a/docs/source/decoders/mwpm.rst +++ b/docs/source/decoders/mwpm.rst @@ -22,6 +22,7 @@ Plotting -------- .. automodule:: opensurfacesim.decoders.mwpm.plot + :member-order: bysource :members: diff --git a/docs/source/decoders/template.rst b/docs/source/decoders/template.rst index 2f4826a2..955550c6 100644 --- a/docs/source/decoders/template.rst +++ b/docs/source/decoders/template.rst @@ -1,12 +1,7 @@ Template decoder ================ -.. autoclass:: opensurfacesim.decoders._template.SimCode - :member-order: bysource - :inherited-members: - :members: - -.. autoclass:: opensurfacesim.decoders._template.PlotCode +.. automodule:: opensurfacesim.decoders._template :member-order: bysource :inherited-members: :members: \ No newline at end of file diff --git a/docs/source/decoders/ufns.rst b/docs/source/decoders/ufns.rst index c00dcc3a..90686c08 100644 --- a/docs/source/decoders/ufns.rst +++ b/docs/source/decoders/ufns.rst @@ -5,8 +5,10 @@ Information :members: .. automodule:: opensurfacesim.decoders.ufns.elements + :member-order: bysource :members: + Simulation ---------- diff --git a/docs/source/decoders/unionfind.rst b/docs/source/decoders/unionfind.rst index 714cbc9a..aead1cee 100644 --- a/docs/source/decoders/unionfind.rst +++ b/docs/source/decoders/unionfind.rst @@ -26,12 +26,16 @@ Plotting .. autoclass:: opensurfacesim.decoders.unionfind.plot.Toric + .. autoclass:: opensurfacesim.decoders.unionfind.plot::Toric.Figure2D + + .. autoclass:: opensurfacesim.decoders.unionfind.plot::Toric.Figure3D + .. autoclass:: opensurfacesim.decoders.unionfind.plot.Planar + .. autoclass:: opensurfacesim.decoders.unionfind.plot::Planar.Figure2D + + .. autoclass:: opensurfacesim.decoders.unionfind.plot::Planar.Figure3D -.. .. autoclass:: opensurfacesim.decoders.unionfind.plot.Toric.Figure2D -.. :members: -.. :inherited-members: .. [delfosse2017almost] Delfosse, Nicolas and Nickerson, Naomi H., *Almost-linear time decoding algorithm for topological codes*, arXiv preprint arXiv:1709.06218, 2017. diff --git a/docs/source/errors/template.rst b/docs/source/errors/template.rst index cd2c52e0..6bde0b87 100644 --- a/docs/source/errors/template.rst +++ b/docs/source/errors/template.rst @@ -2,4 +2,5 @@ Template error -------------- .. automodule:: opensurfacesim.errors._template + :member-order: bysource :members: diff --git a/examples.ipynb b/examples.ipynb index 2ecb5de3..34417e7b 100644 --- a/examples.ipynb +++ b/examples.ipynb @@ -1,65 +1,89 @@ { "cells": [ + { + "source": [ + "# opensurfacesim\n", + "\n", + "Opensurfacesim is a simulation package for the surface code, and is designed to modularize 3 aspects of a surface code simulation.\n", + "\n", + "1. The surface code\n", + "2. The error model\n", + "3. The used decoder\n", + "\n", + "## Examples from the readme.\n", + "\n", + "The included examples in this section uses `opensurfacesim.main.initialize` to setup the surface code and decoder, and `opensurfacesim.main.run` to perform simulations. We'll expand these examples with more in-depth descriptions and how to perform a threshold simulation with `opensurfacesim.main.threshold`. \n", + "\n", + "To simulate the toric code and simulate with bit-flip error for 10 iterations and decode with the MWPM decoder:" + ], + "cell_type": "markdown", + "metadata": {} + }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "tags": [] }, - "outputs": [ - { - "ename": "TclError", - "evalue": "couldn't connect to display \"172.19.64.1:0.0\"", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTclError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mopensurfacesim\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mpf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mopensurfacesim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcodes\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtoric\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFaultyMeasurements\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfigure3d\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mpf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minitialize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"pauli\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mdc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mopensurfacesim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecoders\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmwpm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mToric\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0muse_blossom5\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/mep/opensurfacesim/opensurfacesim/codes/_template/plot.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, figure3d, *args, **kwargs)\u001b[0m\n\u001b[1;32m 320\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure3d\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfigure3d\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[0mTemplateSimFM\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 322\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfigure\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFigure3D\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mfigure3d\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFigure2D\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 323\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 324\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/mep/opensurfacesim/opensurfacesim/codes/toric/plot.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, code, *args, **kwargs)\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmain_boundary\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m0.25\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m0.25\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m0.5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m0.5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/mep/opensurfacesim/opensurfacesim/codes/_template/plot.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, code, *args, **kwargs)\u001b[0m\n\u001b[1;32m 91\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 92\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mTemplateSimPM\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 93\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minit_plot\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 94\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 95\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0merror_methods\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/mep/opensurfacesim/opensurfacesim/plot.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 677\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 678\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 679\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mprojection\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"3d\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 680\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 681\u001b[0m def _init_axis(\n", - "\u001b[0;32m~/mep/opensurfacesim/opensurfacesim/plot.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, init_plot, projection, **kwargs)\u001b[0m\n\u001b[1;32m 179\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 180\u001b[0m \u001b[0;31m# Init figure object\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 181\u001b[0;31m self.figure = plt.figure(\n\u001b[0m\u001b[1;32m 182\u001b[0m \u001b[0mfigsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"scale_figure_length\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"scale_figure_height\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 183\u001b[0m )\n", - "\u001b[0;32m/usr/lib/python3/dist-packages/matplotlib/pyplot.py\u001b[0m in \u001b[0;36mfigure\u001b[0;34m(num, figsize, dpi, facecolor, edgecolor, frameon, FigureClass, clear, **kwargs)\u001b[0m\n\u001b[1;32m 537\u001b[0m \u001b[0mdpi\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m72\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 538\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 539\u001b[0;31m figManager = new_figure_manager(num, figsize=figsize,\n\u001b[0m\u001b[1;32m 540\u001b[0m \u001b[0mdpi\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdpi\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 541\u001b[0m \u001b[0mfacecolor\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mfacecolor\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3/dist-packages/matplotlib/backend_bases.py\u001b[0m in \u001b[0;36mnew_figure_manager\u001b[0;34m(cls, num, *args, **kwargs)\u001b[0m\n\u001b[1;32m 3257\u001b[0m \u001b[0mfig_cls\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'FigureClass'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mFigure\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3258\u001b[0m \u001b[0mfig\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfig_cls\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3259\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mcls\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnew_figure_manager_given_figure\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfig\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3260\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3261\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mclassmethod\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3/dist-packages/matplotlib/backends/_backend_tk.py\u001b[0m in \u001b[0;36mnew_figure_manager_given_figure\u001b[0;34m(cls, num, figure)\u001b[0m\n\u001b[1;32m 948\u001b[0m \"\"\"\n\u001b[1;32m 949\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0m_restore_foreground_window_at_end\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 950\u001b[0;31m \u001b[0mwindow\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtk\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTk\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mclassName\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"matplotlib\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 951\u001b[0m \u001b[0mwindow\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwithdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 952\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.8/tkinter/__init__.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, screenName, baseName, className, useTk, sync, use)\u001b[0m\n\u001b[1;32m 2259\u001b[0m \u001b[0mbaseName\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbaseName\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mext\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2260\u001b[0m \u001b[0minteractive\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2261\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtk\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_tkinter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mscreenName\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbaseName\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mclassName\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minteractive\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwantobjects\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0museTk\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msync\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0muse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2262\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0museTk\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2263\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_loadtk\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mTclError\u001b[0m: couldn't connect to display \"172.19.64.1:0.0\"" - ] - } - ], + "outputs": [], "source": [ - "\n", - "import opensurfacesim\n", - "pf = opensurfacesim.codes.toric.plot.FaultyMeasurements(3, figure3d=True)\n", - "pf.initialize(\"pauli\")\n", - "\n", - "dc = opensurfacesim.decoders.mwpm.plot.Toric(pf, use_blossom5=True)\n" + "from opensurfacesim.main import initialize, run, BenchmarkDecoder\n", + "code, decoder = initialize((6,6), \"toric\", \"mwpm\", enabled_errors=[\"pauli\"])\n", + "run(code, decoder, iterations=10, error_rates = {\"p_bitflip\": 0.1})" ] }, + { + "source": [ + "Benchmarking of decoders can be enabled by attaching a *benchmarker* object to the decoder. See the docs for the syntax and information to setup benchmarking." + ], + "cell_type": "markdown", + "metadata": {} + }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "ename": "TypeError", - "evalue": "'float' object cannot be interpreted as an integer", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/mep/OpenSurfaceSim/opensurfacesim/info/benchmark.py\u001b[0m in \u001b[0;36mwrapper_repeat\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 91\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbenchmarker\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_count\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprocess_time\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mt0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 92\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 93\u001b[0;31m \u001b[0mvalue\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 94\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 95\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mwrapper_repeat\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/mep/OpenSurfaceSim/opensurfacesim/decoders/mwpm/sim.py\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 37\u001b[0m '''\n\u001b[1;32m 38\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_matchings\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 39\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapply_matching\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 40\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 41\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/mep/OpenSurfaceSim/opensurfacesim/decoders/mwpm/sim.py\u001b[0m in \u001b[0;36mapply_matching\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 104\u001b[0m \u001b[0mdy\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0myd\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mdy0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"n\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdy0\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0mdy1\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mdy1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"s\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0mdx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxd\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mdx0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"e\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdx0\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0mdx1\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mdx1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"w\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 106\u001b[0;31m \u001b[0mxv\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwalk_and_flip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mm0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdy\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0myd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 107\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwalk_and_flip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mm1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 108\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/mep/OpenSurfaceSim/opensurfacesim/decoders/mwpm/sim.py\u001b[0m in \u001b[0;36mwalk_and_flip\u001b[0;34m(cls, flipnode, matchnode, length, key)\u001b[0m\n\u001b[1;32m 113\u001b[0m \u001b[0madds\u001b[0m \u001b[0mthis\u001b[0m \u001b[0medge\u001b[0m \u001b[0mto\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mmatching\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 114\u001b[0m '''\n\u001b[0;32m--> 115\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlength\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 116\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mflipnode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mflipedge\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcls\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_neighbor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mflipnode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 117\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mmatchnode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcls\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_neighbor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmatchnode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mTypeError\u001b[0m: 'float' object cannot be interpreted as an integer" - ] - } + "outputs": [], + "source": [ + "benchmarker = BenchmarkDecoder({\"decode\":\"duration\"})\n", + "code, decoder = initialize((6,6), \"toric\", \"mwpm\", enabled_errors=[\"pauli\"])\n", + "run(code, decoder, iterations=10, error_rates = {\"p_bitflip\": 0.1}, benchmark=benchmarker)" + ] + }, + { + "source": [ + "The figures in opensurfacesim allows for step-by-step visualization of the surface code simulation (and if supported the decoding process). Each figure logs its history such that the user can move backwards in time to view past states of the surface (and decoder). Press `h` when the figure is open for more information." ], + "cell_type": "markdown", + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "code, decoder = initialize((6,6), \"toric\", \"mwpm\", enabled_errors=[\"pauli\"], plotting=True, initial_states=(0,0))\n", + "run(code, decoder, error_rates = {\"p_bitflip\": 0.1, \"p_phaseflip\": 0.1}, decode_initial=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "code, decoder = initialize((3,3), \"toric\", \"mwpm\", enabled_errors=[\"pauli\"], faulty_measurements=True, plotting=True, initial_states=(0,0))\n", + "run(code, decoder, error_rates = {\"p_bitflip\": 0.05, \"pm_bitflip\": 0.05}, decode_initial=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ - "dc.decode()" + "print(__file__)" ] }, { @@ -86,9 +110,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.0" + "version": "3.9.0-final" } }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/opensurfacesim/__init__.py b/opensurfacesim/__init__.py index f646b60c..c50502cd 100644 --- a/opensurfacesim/__init__.py +++ b/opensurfacesim/__init__.py @@ -5,4 +5,4 @@ from . import main from . import threshold -__version__ = "0.1.0" +__version__ = "0.1.1" diff --git a/opensurfacesim/__main__.py b/opensurfacesim/__main__.py index ef37cbb6..62c5026f 100644 --- a/opensurfacesim/__main__.py +++ b/opensurfacesim/__main__.py @@ -106,14 +106,14 @@ def cli(args): ["-pe", "--p_erasure", "store", "Erasure rate - float {0,1}", dict(type=float, default=0)], [ "-pmx", - "--pm_bitflip", + "--p_bitflip_plaq", "store", "Measurement bitflip rate - float {0,1}", dict(type=float, default=0), ], [ "-pmz", - "--pm_phaseflip", + "--p_bitflip_star", "store", "Measurement phaseflip rate - float {0,1}", dict(type=float, default=0), @@ -197,14 +197,14 @@ def cli(args): ["-pe", "--p_erasure", "store", "Erasure rate - float {0,1}", dict(type=float, nargs="*")], [ "-pmx", - "--pm_bitflip", + "--p_bitflip_plaq", "store", "Measurement bitflip rate - float {0,1}", dict(type=float, nargs="*"), ], [ "-pmz", - "--pm_phaseflip", + "--p_bitflip_star", "store", "Measurement phaseflip rate - float {0,1}", dict(type=float, nargs="*"), diff --git a/opensurfacesim/codes/_template/plot.py b/opensurfacesim/codes/_template/plot.py index f6929d91..bb4c31e5 100644 --- a/opensurfacesim/codes/_template/plot.py +++ b/opensurfacesim/codes/_template/plot.py @@ -53,7 +53,7 @@ def show_corrected(self, **kwargs): self.plot_ancilla("Decoded.", measure=True) def plot_data(self, iter_name: Optional[str] = None, layer: Optional[float] = None, **kwargs): - """Update plots of all data-qubits. A plot iteration is added if a ``iter_name`` is supplied. See `.plot.Template2D.draw_figure`.""" + """Update plots of all data-qubits. A plot iteration is added if a ``iter_name`` is supplied. See `~.plot.Template2D.draw_figure`.""" if not layer: layer = self.layer for qubit in self.data_qubits[layer].values(): @@ -62,7 +62,7 @@ def plot_data(self, iter_name: Optional[str] = None, layer: Optional[float] = No self.figure.draw_figure(new_iter_name=iter_name) def plot_ancilla(self, iter_name: Optional[str] = None, layer: Optional[float] = None, **kwargs): - """Update plots of all ancilla-qubits. A plot iteration is added if a ``iter_name`` is supplied. See `.plot.Template2D.draw_figure`.""" + """Update plots of all ancilla-qubits. A plot iteration is added if a ``iter_name`` is supplied. See `~.plot.Template2D.draw_figure`.""" if not layer: layer = self.layer for qubit in self.ancilla_qubits[self.layer].values(): @@ -71,7 +71,7 @@ def plot_ancilla(self, iter_name: Optional[str] = None, layer: Optional[float] = self.figure.draw_figure(new_iter_name=iter_name) class Figure(TemplatePlotPM): - """Template surface code plot for perfect measurements. + """Surface code plot for perfect measurements. The inner figure class that plots the surface code based on the ``Qubit.loc`` and ``Qubit.z`` values on the set of ``code.data_qubits``, ``code.ancilla_qubits`` and ``code.pseudo_qubits``. This allows for a high amount of code inheritance. @@ -88,12 +88,11 @@ class Figure(TemplatePlotPM): ---------- error_methods : dict A dictionary of the various error methods loaded in the outer class. - code_params : dict + code_params Additional plotting parameters loaded to the `.plot.PlotParams` instance at ``self.params``. """ - main_boundary = None - code_params = { + code_params: dict = { "data00": { "facecolor": "color_qubit_face", }, @@ -133,14 +132,14 @@ def __init__(self, code: TemplateSimPM, *args, **kwargs) -> None: def init_plot(self, **kwargs): """Plots all elements of the surface code onto the figure. Also takes keyword arguments for `~.codes._template.plot.PerfectMeasurements.Figure.init_legend`. - An additional `matplotlib.widgets.RadioButtons` object is added to the figure which allows for the user to choose one of the loaded errors and apply the error directly to a qubit via `_pick_handler`. This object is added via the `init_plot` method to make sure that the errors are already loaded in `self.code.errors`. The method for each loaded error is saved to `self.error_methods`. + An additional `matplotlib.widgets.RadioButtons` object is added to the figure which allows for the user to choose one of the loaded errors and apply the error directly to a qubit via `_pick_handler`. This object is added via the `init_plot` method to make sure that the errors are already loaded in ``self.code.errors``. The method for each loaded error is saved to ``self.error_methods``. See `.errors._template.Plot` for more information. """ - title = "{} lattice".format(str(self.code.__class__.__module__).split(".")[-2]) + title = "{} code".format(str(self.code.__class__.__module__).split(".")[-2]) self._init_axis(self.main_boundary, title=title, aspect="equal", **kwargs) self.init_legend(ncol=1, **kwargs) for error_module in self.code.errors.values(): - for error_name in error_module.error_methods: + for error_name in error_module.gui_methods: method = getattr(error_module, error_name) self.error_methods[error_name] = method self.interact_axes["error_buttons"] = plt.axes(self.params.axis_radio) @@ -155,7 +154,7 @@ def init_plot(self, **kwargs): def init_legend(self, legend_items: List[Line2D] = [], **kwargs): """Initializes the legend of the main axis of the figure. Also takes keyword arguments for `~matplotlib.axes.Axes.legend`. - The legend of the main axis ``self.main_ax`` consists of a series of `~matplotlib.line.Line2D` objects. The qubit, vertex and stars are always in the legend for a surface code plot. Any error from :doc:`../errors/index/` loaded in the code at ``code.errors`` in de outer class will add an extra element to the legend for differentiation if an error occurs. The 'Line2D' attributes are stored at ``error.Plot.legend_properties`` of the error module. + The legend of the main axis ``self.main_ax`` consists of a series of `~matplotlib.line.Line2D` objects. The qubit, vertex and stars are always in the legend for a surface code plot. Any error from :doc:`../errors/index/` loaded in the code at ``code.errors`` in de outer class will add an extra element to the legend for differentiation if an error occurs. The `~matplotlib.line.Line2D` attributes are stored at ``error.Plot.legend_params`` of the error module (see `.errors._template.Plot`). Parameters ---------- @@ -201,7 +200,7 @@ def init_legend(self, legend_items: List[Line2D] = [], **kwargs): item_names = [] # Error legend items for error in self.code.errors.values(): - for param_name, name in error.legend_names.items(): + for param_name, name in error.legend_titles.items(): if param_name not in item_names: self.lh.append(self._legend_circle(name, **getattr(self.params, param_name))) item_names.append(name) @@ -385,6 +384,22 @@ def _update_data(self, qubit: DataQubit, artist: Optional[Artist] = None, tempor class FaultyMeasurements(PerfectMeasurements, TemplateSimFM): + """Plotting template code class for faulty measurements. + + Inherits from `.codes._template.sim.FaultyMeasurements` and `.codes._template.plot.PerfectMeasurements`. See documentation for these classes for more. + + Dependent on the ``figure3d`` argument, either a 3D figure object is created that inherits from `~.plot.Template3D` and `.codes._template.plot.PerfectMeasurements.Figure`, or the 2D `.codes._template.plot.PerfectMeasurements.Figure` is used. + + Parameters + ---------- + args + Positional arguments are passed on to `.codes._template.sim.FaultyMeasurements`. + figure3d + Enables plotting on a 3D lattice. Disable to plot layer-by-layer on a 2D lattice, which increases responsiveness. + kwargs + Keyword arguments are passed on to `.codes._template.sim.FaultyMeasurements` and the figure object. + + """ def __init__(self, *args, figure3d: bool = True, **kwargs) -> None: self.figure3d = figure3d TemplateSimFM.__init__(self, *args, **kwargs) @@ -408,7 +423,7 @@ def random_measure_layer(self, **kwargs): self.plot_ancilla(f"Layer {self.layer}: ancilla-qubits measured") def plot_data(self, iter_name: Optional[str] = None, **kwargs): - """Update plots of all data-qubits in layer ``z``. A plot iteration is added if a ``iter_name`` is supplied. See `.plot.Template2D.draw_figure`.""" + """Update plots of all data-qubits in ``self.layer``. A plot iteration is added if a ``iter_name`` is supplied. See `.plot.Template2D.draw_figure`.""" for qubit in self.data_qubits[self.layer].values(): artist = qubit.surface_plot if self.figure3d else self.data_qubits[0][qubit.loc].surface_plot self.figure._update_data(qubit, artist, **kwargs) @@ -416,7 +431,7 @@ def plot_data(self, iter_name: Optional[str] = None, **kwargs): self.figure.draw_figure(new_iter_name=iter_name) def plot_ancilla(self, iter_name: Optional[str] = None, **kwargs): - """Update plots of all ancilla-qubits in layer ``z``. A plot iteration is added if a ``iter_name`` is supplied. See `.plot.Template2D.draw_figure`.""" + """Update plots of all ancilla-qubits in ``self.layer``. A plot iteration is added if a ``iter_name`` is supplied. See `.plot.Template2D.draw_figure`.""" for qubit in self.ancilla_qubits[self.layer].values(): artist = qubit.surface_plot if self.figure3d else self.ancilla_qubits[0][qubit.loc].surface_plot self.figure._update_ancilla(qubit, artist) diff --git a/opensurfacesim/codes/_template/sim.py b/opensurfacesim/codes/_template/sim.py index 7fc46593..778b4c9c 100644 --- a/opensurfacesim/codes/_template/sim.py +++ b/opensurfacesim/codes/_template/sim.py @@ -2,13 +2,13 @@ import time from ..elements import DataQubit, AncillaQubit, PseudoQubit, Edge, PseudoEdge from ...errors._template import Sim as Error -from typing import List, Optional, Union, Tuple +from typing import Any, List, Optional, Union, Tuple from collections import defaultdict import importlib class PerfectMeasurements(ABC): - """Simulation template code class for perfect measurements. + """Simulation code class for perfect measurements. The qubits of the code class are stored in a double dictionary, with the keys in the outer dictionary corresponding to the qubit layer. For perfect measurements, there is a single layer. For faulty measurements, there are multiple layers (and defaults to ``self.size``). In the nested dictionaries each qubit is stored by ``qubit.loc`` as key. A qubit can thus be accessed by ``self.qubits[layer][(x,y)]``. @@ -22,19 +22,19 @@ class PerfectMeasurements(ABC): Attributes ---------- ancilla_qubits : dict of dict - Nested dictionary of `~opensurfacesim.code.elements.AncillaQubit`\ s. + Nested dictionary of `~.codes.elements.AncillaQubit` objects. data_qubits : dict of dict - Nested dictionary of `~opensurfacesim.code.elements.DataQubit`\ s. + Nested dictionary of `~.codes.elements.DataQubit` objects. pseudo_qubits : dict of dict - Nested dictionary of `~opensurfacesim.code.elements.PseudoQubit`\ s. + Nested dictionary of `~.codes.elements.PseudoQubit` objects. errors : dict Dictionary of error modules with the module name as key. All error modules from :doc:`../errors/index` loaded in ``self.errors`` will be applied during a simulation by :meth:`random_errors`. logical_operators : dict of list - Dictionary with lists of `~opensurfacesim.code.elements.Edge`\ s that from a trivial loop over the surface and correspond to a logical operator. The logical state of each operator can be obtained by the state of each Edge in the list. + Dictionary with lists of `~.codes.elements.Edge` objects that from a trivial loop over the surface and correspond to a logical operator. The logical state of each operator can be obtained by the state of each Edge in the list. logical_state : dict of bool Dictionary with the states corresponding to the logical operators in ``self.logical_operators``. @@ -49,13 +49,13 @@ class PerfectMeasurements(ABC): Time stamp that is renewed every time `random_errors` is called. Helps with identifying a 'round' of simulation when using class attributes. """ - DataQubit = DataQubit - AncillaQubit = AncillaQubit - PseudoQubit = PseudoQubit - Edge = Edge + _DataQubit = DataQubit + _AncillaQubit = AncillaQubit + _PseudoQubit = PseudoQubit + _Edge = Edge name = "template" - x_names = ["x", "X", 0, "bit-flip"] - z_names = ["z", "Z", 1, "phase-flip"] + x_names = ["x", "X", 0, "bitflip"] + z_names = ["z", "Z", 1, "phaseflip"] dim = 2 def __init__( @@ -68,8 +68,6 @@ def __init__( self.decode_layer = 0 self.size = size if type(size) == tuple else (size, size) self.no_error = True - for key, value in kwargs.items(): - setattr(self, key, value) self.ancilla_qubits = {} self.data_qubits = {} self.pseudo_qubits = defaultdict(dict) @@ -141,29 +139,31 @@ def init_errors(self, *error_modules: Union[str, Error], error_rates: dict = {}, Parameters ---------- - error_modules : string or error class + error_modules The error modules to load. May be a string or an error module from :doc:`../errors/index`. - error_rates : dict of floats + error_rates The default error rates for the loaded modules. Must be a dictionary with probabilities with keywords corresponding to the default or overriding error rates of the associated error modules. Examples -------- - Load :doc:`../errors/pauli` and :doc:`../errors/erasure/` modules via string names. Set default bit-flip rate to `0.1` and erasure to `0.03`. + Load :doc:`../errors/pauli` and :doc:`../errors/erasure/` modules via string names. Set default bitflip rate to `0.1` and erasure to `0.03`. - >>> SurfaceCode.init_errors( + >>> code.init_errors( ... "pauli", ... "erasure", ... error_rates={"p_bitflip": 0.1, "p_erasure": 0.03} ... ) - Load Pauli error module via module. Set default phase-flip rate to `0.05`. + Load Pauli error module via module. Set default phaseflip rate to `0.05`. - >>> import opensurfacesim.errors.pauli as pauli - >>> SurfaceCode.init_errors(pauli, error_rates={"p_phaseflip": 0.05}) + >>> import .errors.pauli as pauli + >>> code.init_errors(pauli, error_rates={"p_phaseflip": 0.05}) """ for error_module in error_modules: if type(error_module) == str: - error_module = importlib.import_module(".errors.{}".format(error_module), package="opensurfacesim") + error_module = importlib.import_module( + ".errors.{}".format(error_module), package="opensurfacesim" + ) self._init_error(error_module, error_rates) def _init_error(self, error_module, error_rates): @@ -178,12 +178,26 @@ def _init_error(self, error_module, error_rates): """ def add_data_qubit( - self, loc: Tuple[float, float], z: float = 0, initial_states: Tuple[float, float] = (None, None), **kwargs + self, + loc: Tuple[float, float], + z: float = 0, + initial_states: Tuple[float, float] = (None, None), + **kwargs, ) -> DataQubit: - """Initializes a `~.code.elements.DataQubit` with `dataQubit` and `edge`, and saved to ``self.data_qubits[z][loc]``.""" - data_qubit = self.DataQubit(loc, z, **kwargs) - data_qubit.edges["x"] = self.Edge(data_qubit, "x", initial_state=initial_states[0], **kwargs) - data_qubit.edges["z"] = self.Edge(data_qubit, "z", initial_state=initial_states[1], **kwargs) + """Initializes a `~.codes.elements.DataQubit` and saved to ``self.data_qubits[z][loc]``. + + Parameters + ---------- + initial_states + Initial state for the data-qubit. + """ + data_qubit = self._DataQubit(loc, z, **kwargs) + data_qubit.edges["x"] = self._Edge( + data_qubit, "x", initial_state=initial_states[0], **kwargs + ) + data_qubit.edges["z"] = self._Edge( + data_qubit, "z", initial_state=initial_states[1], **kwargs + ) self.data_qubits[z][loc] = data_qubit return data_qubit @@ -194,8 +208,8 @@ def add_ancilla_qubit( state_type: str = "x", **kwargs, ) -> AncillaQubit: - """Initializes a `~.code.elements.AncillaQubit` with `ancillaQubit`, and saved to ``self.ancilla_qubits[z][loc]``.""" - ancilla_qubit = self.AncillaQubit(loc, z, state_type=state_type, **kwargs) + """Initializes a `~.codes.elements.AncillaQubit` and saved to ``self.ancilla_qubits[z][loc]``.""" + ancilla_qubit = self._AncillaQubit(loc, z, state_type=state_type, **kwargs) self.ancilla_qubits[z][loc] = ancilla_qubit return ancilla_qubit @@ -206,8 +220,8 @@ def add_pseudo_qubit( state_type: str = "x", **kwargs, ) -> PseudoQubit: - """Initializes a `~.code.elements.PseudoQubit` with `pseudoQubit`, and saved to ``self.pseudo_qubits[z][loc]``.""" - pseudo_qubit = self.PseudoQubit(loc, z, state_type=state_type, **kwargs) + """Initializes a `~.codes.elements.PseudoQubit` and saved to ``self.pseudo_qubits[z][loc]``.""" + pseudo_qubit = self._PseudoQubit(loc, z, state_type=state_type, **kwargs) self.pseudo_qubits[z][loc] = pseudo_qubit return pseudo_qubit @@ -215,11 +229,11 @@ def add_pseudo_qubit( def entangle_pair( data_qubit: DataQubit, ancilla_qubit: AncillaQubit, - key: Union[float, str], + key: Any, edge: Optional[Edge] = None, **kwargs, ): - """Entangles one `~.code.elements.DataQubit` to a `~.code.elements.AncillaQubit` for parity measurement. + """Entangles one `~.codes.elements.DataQubit` to a `~.codes.elements.AncillaQubit` for parity measurement. Parameters ---------- @@ -228,7 +242,7 @@ def entangle_pair( ancilla_qubit Controlled qubit. key - The entanglement is saved by adding the `~.code.elements.DataQubit` to `~.code.elements.AncillaQubit`\ ``.parity_qubits[key]`` + The entanglement is saved by adding the `~.codes.elements.DataQubit` to `~.codes.elements.AncillaQubit`\ ``.parity_qubits[key]`` edge The edge of the data-qubit to entangle to. """ @@ -243,26 +257,31 @@ def entangle_pair( ---------------------------------------------------------------------------------------- """ - def random_errors(self, apply_order: Optional[List[str]] = None, measure: bool = True, **kwargs): + def random_errors( + self, apply_order: Optional[List[str]] = None, measure: bool = True, **kwargs + ): """Applies all errors loaded in ``self.errors`` attribute to layer ``z``. - The random error is applied for each loaded error module by calling ``error_module.random_error()``. If ``apply_order`` is specified, the error modules are applied in order of the error names in the list. If no order is specified, the errors are applied in a random order. Addionally, any error rate can set by supplying the rate as a keyword argument e.g. ``p_bitflip = 0.1``. + The random error is applied for each loaded error module by calling `~.errors._template.Sim.random_error`. If ``apply_order`` is specified, the error modules are applied in order of the error names in the list. If no order is specified, the errors are applied in a random order. Addionally, any error rate can set by supplying the rate as a keyword argument e.g. ``p_bitflip = 0.1``. Parameters ---------- - z : int or float, optional - Layer of qubits of parity measurements. - apply_order : list of string, optional - The order in which the error modules are applied. Items in the list must equal keys in `self.errors` or the names of the loaded error modules. + apply_order + The order in which the error modules are applied. Items in the list must equal keys in ``self.errors`` or the names of the loaded error modules. + measure + Measure ancilla qubits after errors have been simulated. + """ self.instance = time.time() - ordered_errors = [self.errors[name] for name in apply_order] if apply_order else self.errors.values() + ordered_errors = ( + [self.errors[name] for name in apply_order] if apply_order else self.errors.values() + ) for error_class in ordered_errors: for qubit in self.data_qubits[self.layer].values(): error_class.random_error(qubit, **kwargs) if measure: for ancilla in self.ancilla_qubits[self.layer].values(): - ancilla.get_state() + ancilla.measure() @staticmethod def _parse_boundary_coordinates(size, *args: float) -> List[float]: @@ -274,23 +293,21 @@ def _parse_boundary_coordinates(size, *args: float) -> List[float]: class FaultyMeasurements(PerfectMeasurements): - """Simulation template code class for faulty measurements. + """Simulation code class for faulty measurements. A 3D graph is initiated with ``layers`` amount of 2D surfaces from `~.codes._template.PerfectMeasurement` stacked on top of each other. The structure of the ``self.data_qubits``, ``self.ancilla_qubits`` and ``self.pseudo_qubits`` dictionary attributes allows for the storage for various time instances of the same qubits in the first nested layer. E.g. ``self.data_qubits[0][(0,0)]`` and ``self.data_qubits[1][(0,0)]`` store the data-qubit at (0,0) at time instances 0 and 1, respectively. Consecutive instances of `~.codes.elements.AncillaQubit`\ s and `~.codes.elements.PseudoQubit`\ s are connected in the 3D graph by `~.codes.elements.PseudoEdge` objects. Parameters ---------- - layers : int, optional + layers Number of layers in 3D graph for faulty measurements. - pseudoEdge : PseudoEdge, optional - Pseudo edge class. - pm_bitflip : float or int, optional - Default bit-flip rate during measurements. - pm_phaseflip : float or int, optional - Default phase-flip rate during measurements. + p_bitflip_plaq + Default bitflip rate during measurements on plaquette operators (XXXX). + p_bitflip_star + Default bitflip rate during measurements on star operators (ZZZZ). """ - pseudoEdge = PseudoEdge + _PseudoEdge = PseudoEdge dim = 3 def __init__( @@ -298,8 +315,8 @@ def __init__( size, *args, layers: Optional[int] = None, - pm_bitflip: float = 0, - pm_phaseflip: float = 0, + p_bitflip_plaq: float = 0, + p_bitflip_star: float = 0, **kwargs, ): super().__init__(size, *args, **kwargs) @@ -309,7 +326,7 @@ def __init__( else: self.layers = max(size) if type(size) == tuple else size self.decode_layer = self.layers - 1 - self.default_faulty_measurements = dict(pm_bitflip=pm_bitflip, pm_phaseflip=pm_phaseflip) + self.default_faulty_measurements = dict(p_bitflip_plaq=p_bitflip_plaq, p_bitflip_star=p_bitflip_star) """ ---------------------------------------------------------------------------------------- @@ -320,14 +337,14 @@ def __init__( def simulate(self, **kwargs): """Simulate an iteration or errors and measurement. - On all but the final layer, the default or overriding error rates (via keyworded arguments) are applied. On the final layer, perfect measurements are applied by setting `pm_bitflip=0` and `pm_phaseflip=0`. + On all but the final layer, the default or overriding error rates (via keyworded arguments) are applied. On the final layer, perfect measurements are applied by setting ``p_bitflip_plaq=0`` and ``p_bitflip_star=0``. """ # Simulate on all but final layers for z in range(self.layers): super().simulate(z=z, **kwargs) # Simulate final layer with perfect measurements - kwargs.update(dict(pm_bitflip=0, pm_phaseflip=0)) + kwargs.update(dict(p_bitflip_plaq=0, p_bitflip_star=0)) super().simulate(z=self.decode_layer, **kwargs) """ @@ -339,11 +356,11 @@ def simulate(self, **kwargs): def init_surface(self, **kwargs): """Initiates the surface code. - The 3D lattice is initilized by first building the ground layer. After that each consecutive layer is built and pseudo-edges are added to connect the ancilla qubits of each layer. + The 3D lattice is initialized by first building the ground layer. After that each consecutive layer is built and pseudo-edges are added to connect the ancilla qubits of each layer. """ - super().init_surface() + super().init_surface(z=0, **kwargs) for z in range(1, self.layers): - super().init_surface(z=z) + super().init_surface(z=z, **kwargs) for upper in self.ancilla_qubits[z].values(): lower = self.ancilla_qubits[z - 1][upper.loc] self.add_vertical_edge(lower, upper) @@ -369,9 +386,9 @@ def add_vertical_edge( Newer instance of ancilla-qubit. """ - pseudo_edge = self.pseudoEdge(upper_ancilla, state_type=upper_ancilla.state_type) - upper_ancilla.vertical_edges[lower_ancilla] = pseudo_edge - lower_ancilla.vertical_edges[upper_ancilla] = pseudo_edge + pseudo_edge = self._PseudoEdge(upper_ancilla, state_type=upper_ancilla.state_type) + upper_ancilla.z_neighbors[lower_ancilla] = pseudo_edge + lower_ancilla.z_neighbors[upper_ancilla] = pseudo_edge pseudo_edge.nodes = [upper_ancilla, lower_ancilla] """ @@ -382,8 +399,8 @@ def add_vertical_edge( def random_errors( self, - pm_bitflip: Optional[float] = None, - pm_phaseflip: Optional[float] = None, + p_bitflip_plaq: Optional[float] = None, + p_bitflip_star: Optional[float] = None, **kwargs, ): @@ -391,21 +408,21 @@ def random_errors( Parameters ---------- - z : int or float, optional - Layer of qubits. - pm_bitflip : int or float, optional - Probability of a bit-flip during a parity check measurement. + p_bitflip_plaq : int or float, optional + Probability of a bitflip during a parity check measurement on plaquette operators (XXXX). + p_bitflip_star : int or float, optional + Probability of a bitflip during a parity check measurement on star operators (ZZZZ). """ - if pm_bitflip is None: - pm_bitflip = self.default_faulty_measurements["pm_bitflip"] - if pm_phaseflip is None: - pm_phaseflip = self.default_faulty_measurements["pm_phaseflip"] + if p_bitflip_plaq is None: + p_bitflip_plaq = self.default_faulty_measurements["p_bitflip_plaq"] + if p_bitflip_star is None: + p_bitflip_star = self.default_faulty_measurements["p_bitflip_star"] for ancilla in self.ancilla_qubits[self.layers - 1].values(): ancilla.measured_state = False for z in range(self.layers - 1): self.layer = z self.random_errors_layer(**kwargs) - self.random_measure_layer(pm_bitflip=pm_bitflip, pm_phaseflip=pm_phaseflip) + self.random_measure_layer(p_bitflip_plaq=p_bitflip_plaq, p_bitflip_star=p_bitflip_star) self.layer = self.layers - 1 self.random_errors_layer(**kwargs) self.random_measure_layer() @@ -434,5 +451,5 @@ def random_measure_layer(self, **kwargs): """ for ancilla in self.ancilla_qubits[self.layer].values(): previous_ancilla = self.ancilla_qubits[(ancilla.z - 1) % self.layers][ancilla.loc] - measured_state = ancilla.get_state(**kwargs) + measured_state = ancilla.measure(**kwargs) ancilla.syndrome = measured_state != previous_ancilla.measured_state diff --git a/opensurfacesim/codes/elements.py b/opensurfacesim/codes/elements.py index 9fead9af..d16cb596 100644 --- a/opensurfacesim/codes/elements.py +++ b/opensurfacesim/codes/elements.py @@ -7,7 +7,7 @@ class Qubit(ABC): """General type qubit object. - This class mainly serves as a superclass or template to other more useful qubit types, which have the apprioate subclass attributes and subclass methods. For other types to to the 'See Also' section. + # This class mainly serves as a superclass or template to other more useful qubit types, which have the apprioate subclass attributes and subclass methods. For other types to to the 'See Also' section. Parameters ---------- @@ -31,12 +31,12 @@ def __repr__(self): class DataQubit(Qubit): """Data type qubit object. - The state of a data-qubit is determined by two `~opensurfacesim.codes._template.Edge` objects stored in the `self.edges` dictionary. Each of the edges are part of a separate graph on the surface lattice. + The state of a data-qubit is determined by two `~.codes.elements.Edge` objects stored in the ``self.edges`` dictionary. Each of the edges are part of a separate graph on the surface lattice. Attributes ---------- - edges : dict of `.Edge` + edges : dict of `~.codes.elements.Edge` Dictionary of edges with the error type as key (e.g. ``"x"`` or ``"z"``). self.edges = {"x": Edge_x, "z", Edge_z} @@ -83,21 +83,21 @@ class AncillaQubit(Qubit): """ General type qubit object. - An ancilla-qubit is entangled to one or more `~opensurfacesim.codes._template.DataQubit` objects. The `self.state_type` attribute determines the state on which the measurement is applied. A single measurement is applied when the class property `self.state` is called. The state of the last measurement is stored in `self.measured_state` for state access without prompting a new measurement. + An ancilla-qubit is entangled to one or more `~codes.elements.DataQubit` objects. The ``self.state_type`` attribute determines the state on which the measurement is applied. A single measurement is applied when the class property ``self.state`` is called. The state of the last measurement is stored in ``self.measured_state`` for state access without prompting a new measurement. Parameters ---------- state_type : str, {"x", "z"} - Type of '.Edge' objects belonging to the '~opensurfacesim.codes._template.DataQubit` objects entangled to the current ancilla-qubit for stabilizer measurements. + Type of 'codes.elements.Edge' objects belonging to the `~.codes.elements.DataQubit` objects entangled to the current ancilla-qubit for stabilizer measurements. Attributes ---------- - parity_qubits : dict of `~opensurfacesim.codes._template.DataQubit` + parity_qubits : dict of `~.codes.elements.DataQubit` All qubits in this dictionary are entangled to the current ancilla for stabilizer measurements. - vertical_edges : dict of `~opensurfacesim.codes._template.PseudoEdge` - Vertically connected ancilla that is an instance of the same qubit at a different time, required for faulty measurements. Instances at *u* or *up* refer to instances later in time, and instances at *d* or *down* refer to an instances prior in time. + z_neighbors : {`.codes.elements.AncillaQubit`: `~.codes.elements.PseudoEdge`} + Neighbor ancilla in the z direction that is an instance of the same qubit at a different time, required for faulty measurements. state : bool - Property that measures the parity of the qubits in `self.parity_qubits`. + Property that measures the parity of the qubits in ``self.parity_qubits``. measured_state : bool The result of the last parity measurement. syndrome : bool @@ -107,7 +107,7 @@ class AncillaQubit(Qubit): Examples -------- - The state of the entangled `~opensurfacesim.codes._template.DataQubit` is located at: + The state of the entangled `~.codes.elements.DataQubit` is located at: >>> AncillaQubit.parity_qubits[key].edges[AncillaQubit.state_type] True @@ -121,29 +121,31 @@ def __init__(self, *args, state_type: str = "default", **kwargs): self.measured_state = False self.syndrome = False self.parity_qubits = {} - self.vertical_edges = {} + self.z_neighbors = {} self.measurement_error = False @property def state(self): - return self.get_state() + return self.measure() - def get_state(self, pm_bitflip: float = 0, pm_phaseflip: float = 0, **kwargs) -> bool: - """Applies a parity measurement on the ancilla with probability ``p_bitflip``. + def measure(self, p_bitflip_plaq: float = 0, p_bitflip_star: float = 0, **kwargs) -> bool: + """Applies a parity measurement on the ancilla. The functions loops over all the data qubits in ``self.parity_qubits``. For every edge associated with the entangled state on the data qubit, the value of a ``parity`` boolean is flipped. Parameters ---------- - p_bitflip : float - Bitflip probability. + p_bitflip_plaq : float + Bitflip rate for plaquette (XXXX) operators. + p_bitflip_star : float + Bitflip rate for star (ZZZZ) operators. """ parity = False for data_qubit in self.parity_qubits.values(): if data_qubit.state[self.state_type]: parity = not parity - p_measure = pm_bitflip if self.state_type == "x" else pm_phaseflip + p_measure = p_bitflip_plaq if self.state_type == "x" else p_bitflip_star self.measurement_error = p_measure != 0 and random.random() < p_measure if self.measurement_error: parity = not parity @@ -155,9 +157,9 @@ def get_state(self, pm_bitflip: float = 0, pm_phaseflip: float = 0, **kwargs) -> class Edge(object): - """A state object belonging to a `~opensurfacesim.codes._template.DataQubit` object. + """A state object belonging to a `~.codes.elements.DataQubit` object. - An edge cannot have open vertices and must be spanned by two nodes. In this case, the two nodes must be `~opensurfacesim.codes._template.AncillaQubit` objects, and are stored in ``self.nodes``. + An edge cannot have open vertices and must be spanned by two nodes. In this case, the two nodes must be `~.codes.elements.AncillaQubit` objects, and are stored in ``self.nodes``. Parameters ---------- @@ -170,7 +172,7 @@ class Edge(object): Attributes ---------- - nodes : list of two ~opensurfacesim.codes._template.AncillaQubit` objects + nodes : list of two ~.codes.elements.AncillaQubit` objects The vertices that spans the edge. state : bool The current quantum state on the edge object. @@ -219,7 +221,7 @@ def add_node(self, node: AncillaQubit, **kwargs): class PseudoQubit(AncillaQubit): - """Boundary element, imitates `.AncillaQubit`. + """Boundary element, imitates `.codes.elements.AncillaQubit`. Edges needs to be spanned by two nodes. For data qubits on the boundary, one of its edges additionally requires an ancilla qubit like node, which is the pseudo-qubit. """ @@ -228,6 +230,6 @@ class PseudoQubit(AncillaQubit): class PseudoEdge(Edge): - """Vertical edge connecting time instances of ancilla-qubits, imitates `.Edge`.""" + """Vertical edge connecting time instances of ancilla-qubits, imitates `.codes.elements.Edge`.""" edge_type, rep = "pseudo", "|" diff --git a/opensurfacesim/codes/planar/plot.py b/opensurfacesim/codes/planar/plot.py index 524c8417..c723d53a 100644 --- a/opensurfacesim/codes/planar/plot.py +++ b/opensurfacesim/codes/planar/plot.py @@ -3,10 +3,10 @@ class PerfectMeasurements(SimPM, TemplatePM): - """Plotting planar code class for perfect measurements.""" + # Inherited docstring class Figure(TemplatePM.Figure): - """Planar code plot for perfect measurements.""" + # Inherited docstring def __init__(self, code, *args, **kwargs) -> None: self.main_boundary = [0.25, -0.25, code.size[0] - 0.5, code.size[1] - 0.5] @@ -14,8 +14,26 @@ def __init__(self, code, *args, **kwargs) -> None: class FaultyMeasurements(SimFM, TemplateFM): + """Plotting code class for faulty measurements. + + Inherits from `.codes.planar.sim.FaultyMeasurements` and `.codes.planar.plot.PerfectMeasurements`. See documentation for these classes for more. + + Dependent on the ``figure3d`` argument, either a 3D figure object is created that inherits from `~.plot.Template3D` and `.codes.planar.plot.PerfectMeasurements.Figure`, or the 2D `.codes.planar.plot.PerfectMeasurements.Figure` is used. + + Parameters + ---------- + args + Positional arguments are passed on to `.codes.planar.sim.FaultyMeasurements`. + figure3d + Enables plotting on a 3D lattice. Disable to plot layer-by-layer on a 2D lattice, which increases responsiveness. + kwargs + Keyword arguments are passed on to `.codes.planar.sim.FaultyMeasurements` and the figure object. + """ + class Figure2D(PerfectMeasurements.Figure, TemplateFM.Figure2D): + # Inherited docstring pass class Figure3D(PerfectMeasurements.Figure, TemplateFM.Figure3D): + # Inherited docstring pass diff --git a/opensurfacesim/codes/planar/sim.py b/opensurfacesim/codes/planar/sim.py index d7d5a9fd..8b560d33 100644 --- a/opensurfacesim/codes/planar/sim.py +++ b/opensurfacesim/codes/planar/sim.py @@ -3,7 +3,7 @@ class PerfectMeasurements(ToricPM): - """Simulation planar code for perfect measurements.""" + # Inherited docstring name = "planar" @@ -74,6 +74,6 @@ def init_logical_operator(self, **kwargs): class FaultyMeasurements(ToricFM, PerfectMeasurements): - """Simulation planar code for faulty measurements.""" + # Inherited docstring pass diff --git a/opensurfacesim/codes/toric/plot.py b/opensurfacesim/codes/toric/plot.py index 6a62b7d3..c5f035a8 100644 --- a/opensurfacesim/codes/toric/plot.py +++ b/opensurfacesim/codes/toric/plot.py @@ -3,10 +3,10 @@ class PerfectMeasurements(SimPM, PlotPM): - """Plotting toric code class for perfect measurements.""" + # Inherited docstring class Figure(PlotPM.Figure): - """Toric code plot for perfect measurements.""" + # Inherited docstring def __init__(self, code, *args, **kwargs) -> None: self.main_boundary = [-0.25, -0.25, code.size[0] + 0.5, code.size[1] + 0.5] @@ -14,8 +14,26 @@ def __init__(self, code, *args, **kwargs) -> None: class FaultyMeasurements(SimFM, PlotFM): + """Plotting code class for faulty measurements. + + Inherits from `.codes.toric.sim.FaultyMeasurements` and `.codes.toric.plot.PerfectMeasurements`. See documentation for these classes for more. + + Dependent on the ``figure3d`` argument, either a 3D figure object is created that inherits from `~.plot.Template3D` and `.codes.toric.plot.PerfectMeasurements.Figure`, or the 2D `.codes.toric.plot.PerfectMeasurements.Figure` is used. + + Parameters + ---------- + args + Positional arguments are passed on to `.codes.toric.sim.FaultyMeasurements`. + figure3d + Enables plotting on a 3D lattice. Disable to plot layer-by-layer on a 2D lattice, which increases responsiveness. + kwargs + Keyword arguments are passed on to `.codes.toric.sim.FaultyMeasurements` and the figure object. + + """ class Figure2D(PerfectMeasurements.Figure, PlotFM.Figure2D): + # Inherited docstring pass class Figure3D(PerfectMeasurements.Figure, PlotFM.Figure3D): + # Inherited docstring pass diff --git a/opensurfacesim/codes/toric/sim.py b/opensurfacesim/codes/toric/sim.py index 998b5626..fc27eb58 100644 --- a/opensurfacesim/codes/toric/sim.py +++ b/opensurfacesim/codes/toric/sim.py @@ -3,7 +3,7 @@ class PerfectMeasurements(TemplatePM): - """Simulation toric code for perfect measurements.""" + # Inherited docstring name = "toric" @@ -81,6 +81,5 @@ def _parse_boundary_coordinates(size, *args): class FaultyMeasurements(TemplateFM, PerfectMeasurements): - """Simulation toric code for faulty measurements.""" - + # Inherited docstring pass diff --git a/opensurfacesim/decoders/_template.py b/opensurfacesim/decoders/_template.py index ad2ae6df..bd1508c8 100644 --- a/opensurfacesim/decoders/_template.py +++ b/opensurfacesim/decoders/_template.py @@ -57,6 +57,8 @@ def read_config(path: Path, config_dict: Optional[dict] = None) -> dict: -------- Let us look at the following example INI file. + .. code-block:: text + [main] param1 = hello @@ -115,7 +117,7 @@ def init_config(ini_file, write: bool = False, **kwargs): return config_dict -class SimCode(ABC): +class Sim(ABC): """ Decoder simulation class template. @@ -128,7 +130,7 @@ class SimCode(ABC): Attributes ---------- - compatibililty_measurements : dict + compatibility_measurements : dict Compatibility with perfect or faulty measurements. compatibility_errors : dict Compatibility with the various error modules in :doc:`../errors/index`. @@ -201,12 +203,12 @@ def get_neighbors(self, ancilla_qubit: AncillaQubit, loop: bool = False, **kwarg Parameters ---------- loop - Include looped neighbors. + Include neighbors in time that are not chronologically next to each other during decoding within the same instance. """ neighbors = {} for key in ancilla_qubit.parity_qubits: neighbors[key] = self.get_neighbor(ancilla_qubit, key) - for ancilla, edge in ancilla_qubit.vertical_edges.items(): + for ancilla, edge in ancilla_qubit.z_neighbors.items(): if loop or abs(ancilla.z - ancilla_qubit.z) == 1: neighbors[ancilla.z - ancilla_qubit.z] = (ancilla, edge) return neighbors @@ -217,7 +219,7 @@ def correct_edge(self, ancilla_qubit: AncillaQubit, key: str, **kwargs) -> Ancil The correction is applied to the data-qubit located at ``ancilla_qubit.parity_qubits[key]``. More specifically, the correction is applied to the `~.codes.elements.Edge` object corresponding to the ``state_type`` of ``ancilla_qubit``. """ (next_qubit, edge) = self.get_neighbor(ancilla_qubit, key) - edge.state = 1 - edge.state + edge.state = not edge.state return next_qubit def get_syndrome(self, find_pseudo: bool = False) -> Union[Tuple[LA, LA], Tuple[LTAP, LTAP]]: @@ -268,10 +270,24 @@ def decode(self, *args, **kwargs): pass -class PlotCode(SimCode): +class Plot(Sim): """Decoder plotting class template. - The plotting class initiates a `opensurfacesim.plot` object. For its usage, see :ref:`plot-usage`. + The plotting decoder class requires a surface code object that inherits from `.codes._template.plot.PerfectMeasurements`. The template decoder provides the `plot_matching_edge` method that is called by `correct_edge` to visualize the matched edges on the lattice. + + parameters + ---------- + args, kwargs + Positional and keyword arguments are passed on to `~.decoders._template.Sim`. + + attributes + ---------- + line_color_match : dict + Plot properties for matched edges. + line_color_normal : dict + Plot properties for normal edges. + matching_lines : defaultdict(bool) + Dictionary of edges that have been added to the matching. """ name = ("Template plot decoder",) @@ -282,14 +298,14 @@ def __init__(self, *args, **kwargs) -> None: if hasattr(self.code, "figure"): self.params = self.code.figure.params - self.line_color_normal = { - "x": {"color": self.params.color_edge}, - "z": {"color": self.params.color_edge}, - } self.line_color_match = { "x": {"color": self.params.color_x_secondary}, "z": {"color": self.params.color_z_secondary}, } + self.line_color_normal = { + "x": {"color": self.params.color_edge}, + "z": {"color": self.params.color_edge}, + } self.matching_lines = defaultdict(bool) def decode(self, *args, **kwargs): @@ -301,8 +317,7 @@ def decode(self, *args, **kwargs): def correct_edge(self, qubit, key, **kwargs): # Inherited docstring - (next_qubit, edge) = self.get_neighbor(qubit, key) - edge.state = not edge.state + next_qubit = super().correct_edge(qubit, key, **kwargs) if hasattr(qubit, "surface_lines"): self.plot_matching_edge(qubit.surface_lines.get(key, None)) if hasattr(next_qubit, "surface_lines"): diff --git a/opensurfacesim/decoders/mwpm/__init__.py b/opensurfacesim/decoders/mwpm/__init__.py index b5f9ee94..0208667e 100644 --- a/opensurfacesim/decoders/mwpm/__init__.py +++ b/opensurfacesim/decoders/mwpm/__init__.py @@ -17,13 +17,21 @@ def get_blossomv(accept: bool = False): - """Downloads and compiles the BlossomV algorithm for OpenSurfaceSim.""" + """Downloads and compiles the BlossomV algorithm, which is distributed under the following license: + + License: + + .. include:: ../../../opensurfacesim/decoders/mwpm/blossom5/LICENSE + :literal: + """ folder = os.path.dirname(os.path.abspath(__file__)) try: with open(folder + "/blossom5/LICENSE", "r") as licensefile: - print(licensefile.read()) + lines = "_"*49 + print(f"The Blossom V algorithm is distributed under a different license:\n{lines}\n") + print(licensefile.read(), end=f"{lines}\n") except FileNotFoundError: raise FileNotFoundError("License file missing. Automatic download is disabled.") diff --git a/opensurfacesim/decoders/mwpm/blossom5/LICENSE b/opensurfacesim/decoders/mwpm/blossom5/LICENSE index 8aca7d2a..45e1eade 100644 --- a/opensurfacesim/decoders/mwpm/blossom5/LICENSE +++ b/opensurfacesim/decoders/mwpm/blossom5/LICENSE @@ -1,30 +1,24 @@ -The Blossom V algorithm is distributed under a different license: -_________________________________________________________________________________________ - - Copyright 2008-2009 UCL Business PLC, Author Vladimir Kolmogorov (vnk@ist.ac.at) - - This software can be used for evaluation and non-commercial research purposes only. Commercial use is prohibited. - Public redistribution of the code or its derivatives is prohibited. - If you use this software for research purposes, you should cite the following paper in any resulting publication: - - Vladimir Kolmogorov. "Blossom V: A new implementation of a minimum cost perfect matching algorithm." - In Mathematical Programming Computation (MPC), July 2009, 1(1):43-67. - - For commercial use of the software not covered by this agreement, you may obtain a licence from - the copyright holders UCL Business via their licensing site: www.e-lucid.com/i/software/optimisation_software/BlossomV.html. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -_________________________________________________________________________________________ - +Copyright 2008-2009 UCL Business PLC, Author Vladimir Kolmogorov (vnk@ist.ac.at) + +This software can be used for evaluation and non-commercial research purposes only. Commercial use is prohibited. +Public redistribution of the code or its derivatives is prohibited. +If you use this software for research purposes, you should cite the following paper in any resulting publication: + + Vladimir Kolmogorov. "Blossom V: A new implementation of a minimum cost perfect matching algorithm." + In Mathematical Programming Computation (MPC), July 2009, 1(1):43-67. + +For commercial use of the software not covered by this agreement, you may obtain a licence from +the copyright holders UCL Business via their licensing site: www.e-lucid.com/i/software/optimisation_software/BlossomV.html. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/opensurfacesim/decoders/mwpm/plot.py b/opensurfacesim/decoders/mwpm/plot.py index 13779bc5..a4282e2f 100644 --- a/opensurfacesim/decoders/mwpm/plot.py +++ b/opensurfacesim/decoders/mwpm/plot.py @@ -1,20 +1,24 @@ from .sim import Toric as SimToric, Planar as SimPlanar -from .._template import PlotCode +from .._template import Plot -class Toric(PlotCode, SimToric): +class Toric(Plot, SimToric): """Plot MWPM decoder for the toric code. - - Attributes + + Parameters ---------- - opposite_keys : dict - Dictionary of opposite keys in `~.codes.elements.AncillaQubit`\ ``parity.qubits`` that aids plotting of matching edges. + args, kwargs + Positional and keyword arguments are passed on to `.decoders._template.Plot` and `.decoders.mwpm.sim.Toric`. """ - - opposite_keys = dict(n="s", s="n", e="w", w="e") + pass class Planar(Toric, SimPlanar): - """Plot MWPM decoder for the planar code.""" - + """Plot MWPM decoder for the planar code. + + Parameters + ---------- + args, kwargs + Positional and keyword arguments are passed on to `~.decoders.mwpm.plot.Toric` and `.decoders.mwpm.sim.Planar`. + """ pass diff --git a/opensurfacesim/decoders/mwpm/sim.py b/opensurfacesim/decoders/mwpm/sim.py index 5a6bc773..849f131a 100644 --- a/opensurfacesim/decoders/mwpm/sim.py +++ b/opensurfacesim/decoders/mwpm/sim.py @@ -1,6 +1,6 @@ from typing import List, Tuple from opensurfacesim.codes.elements import AncillaQubit -from .._template import SimCode +from .._template import Sim import networkx as nx from numpy.ctypeslib import ndpointer import ctypes @@ -10,8 +10,14 @@ LA = List[AncillaQubit] -class Toric(SimCode): - """Minimum-Weight Perfect Matching decoder for the toric lattice.""" +class Toric(Sim): + """Minimum-Weight Perfect Matching decoder for the toric lattice. + + Parameters + ---------- + args, kwargs + Positional and keyword arguments are passed on to `.decoders._template.Sim`. + """ name = "Minimum-Weight Perfect Matching" short = "mwpm" diff --git a/opensurfacesim/decoders/ufns/elements.py b/opensurfacesim/decoders/ufns/elements.py index 3323fe02..7cc855ba 100644 --- a/opensurfacesim/decoders/ufns/elements.py +++ b/opensurfacesim/decoders/ufns/elements.py @@ -10,7 +10,7 @@ class Node(ABC): A subgraph :math:`\mathcal{V}\subseteq C` is a spanning-tree of a cluster :math:`C` if it is a connected acyclic subgraph that includes all vertices of :math:`C` and a minimum number of edges. We call the spanning-tree of a cluster its ancilla-tree. An acyclic connected spanning-forest is required for the Union-Find Decoder. - A node-tree :math:`\mathcal{N}` is a partition of a ancilla-tree :math:`\mathcal{V}`, such that each element of the partition -- which we call a *node* :math:`n` -- represents a set of adjacent vertices that lie at the same distance -- the *node radius} :math:`r` -- from the *primer ancilla*, which initializes the node and lies at its center. The node-tree is a directed acyclic graph, and its edges :math:`\mathcal{E}_i` have lengths equal to the distance between the primer vertices of neighboring nodes. + A node-tree :math:`\mathcal{N}` is a partition of a ancilla-tree :math:`\mathcal{V}`, such that each element of the partition -- which we call a *node* :math:`n` -- represents a set of adjacent vertices that lie at the same distance -- the *node radius :math:`r` -- from the *primer ancilla*, which initializes the node and lies at its center. The node-tree is a directed acyclic graph, and its edges :math:`\mathcal{E}_i` have lengths equal to the distance between the primer vertices of neighboring nodes. Parameters ---------- @@ -131,7 +131,7 @@ def ns_parity(self, parent_node: Optional[Node] = None) -> int: Tail recursive function that calculates the parities of the current node and all its children. - .. math:: j_p &= 1 - \big(\sum_{\mathclap{n \in \text{ children of } j}} (1+n_p) \big) \bmod 2. + .. math:: j_p = 1 - \\big(\\sum_{n \\in \\text{ children of } j} (1+n_p) \\big) \\bmod 2. Parameters ---------- @@ -142,8 +142,8 @@ def ns_parity(self, parent_node: Optional[Node] = None) -> int: return self.parity -class Boundary(Node): - short = "B" +class OddNode(Node): + short = "O" def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) @@ -154,19 +154,18 @@ def ns_parity(self, *args, **kwargs) -> int: return self.parity -class Filler(Node): - short = "F" - - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self.parity = 1 - - def ns_parity(self, *args, **kwargs) -> int: - # Inherited docstring - return self.parity +def print_tree(current_node: Node, parent_node: Optional[Node] = None): + """Prints the node-tree of ``current_node`` and its descendents. + Utilizes `pptree `_ to print a tree of nodes, which requires a list of children elements per node. Since the node-tree is semi-directional (the root can be any element in the tree), we need to traverse the node-tree from ``current_node`` in all directions except for the ``parent_node`` to find the children attributes for the current direction. -def print_tree(current_node, parent_node: Optional[Node] = None): + Parameters + ---------- + current_node + Current root of the node-tree to print. + parent_node + Parent node which will not be printed. s + """ def get_children(node, parent=None): node.children = [child for child, _ in node.neighbors if child is not parent] for child in node.children: diff --git a/opensurfacesim/decoders/ufns/plot.py b/opensurfacesim/decoders/ufns/plot.py index 16ec5f04..b396883f 100644 --- a/opensurfacesim/decoders/ufns/plot.py +++ b/opensurfacesim/decoders/ufns/plot.py @@ -6,7 +6,7 @@ class Toric(PlotToric, SimToric): """Union-Find Node-Suspension decoder for the toric lattice with union-find plot. - Has all class attributes and methods from `.ufns.sim.Toric`, with additional parameters below. Default values for these parameters can be supplied via a *decoders.ini* file under the section of ``[ufns]``. + Has all class attributes, methods, and nested figure classes from `.ufns.sim.Toric`, with additional parameters below. Default values for these parameters can be supplied via a *decoders.ini* file under the section of ``[ufns]`` (see `.decoders._template.read_config`). The plotting class initiates a `opensurfacesim.plot` object. For its usage, see :ref:`plot-usage`. @@ -44,7 +44,7 @@ def _pick_handler(self, event): class Planar(Toric, PlotPlanar, SimPlanar): """Union-Find Node-Suspension decoder for the planar lattice with union-find plot. - Has all class attributes and methods from `.ufns.sim.Planar`, with additional parameters below. Default values for these parameters can be supplied via a *decoders.ini* file under the section of ``[ufns]``. + Has all class attributes, methods, and nested figure classes from `.ufns.sim.Planar`, with additional parameters below. Default values for these parameters can be supplied via a *decoders.ini* file under the section of ``[ufns]`` (see `.decoders._template.read_config`). The plotting class initiates a `opensurfacesim.plot` object. For its usage, see :ref:`plot-usage`. diff --git a/opensurfacesim/decoders/ufns/sim.py b/opensurfacesim/decoders/ufns/sim.py index f7ed6ff8..54f9e757 100644 --- a/opensurfacesim/decoders/ufns/sim.py +++ b/opensurfacesim/decoders/ufns/sim.py @@ -2,7 +2,7 @@ from ...codes.elements import AncillaQubit, Edge from ..unionfind.sim import Toric as UFToric, Planar as UFPlanar from ..unionfind.elements import Cluster -from .elements import Node, Syndrome, Junction, Boundary, Filler, print_tree +from .elements import Node, Syndrome, Junction, OddNode, print_tree UL = List[Tuple[AncillaQubit, Edge, AncillaQubit]] @@ -14,7 +14,7 @@ class Toric(UFToric): The boundary of every cluster is not stored at the cluster object, but divided under its partitioned nodes. Cluster growth is initiated from the root of the node-tree. The attributes ``root_node`` and ``min_delay`` are monkey-patched to the `~.unionfind.elements.Cluster` object to assist with cluster growth in the Node-Suspension data structure. See `grow_node` for more. - The current class inherits from `.unionfind.sim.Toric` for its application the Union-Find data structure for cluster growth and mergers. To maintain low operating complexity in UFNS, the following parameters are set of the Union-Find super. + The current class inherits from `.unionfind.sim.Toric` for its application the Union-Find data structure for cluster growth and mergers. To maintain low operating complexity in UFNS, the following parameters are set of the Union-Find parent class. ================= ======= parameter value @@ -33,11 +33,9 @@ class Toric(UFToric): name = "Union-Find Node-Suspension" short = "ufns" - Node = Node - SyndromeNode = Syndrome - JunctionNode = Junction - BoundaryNode = Boundary - FillerNode = Filler + _Syndrome = Syndrome + _Junction = Junction + _OddNode = OddNode compatibility_measurements = dict( PerfectMeasurements=True, @@ -58,9 +56,9 @@ def __init__(self, *args, **kwargs) -> None: ) super().__init__(*args, **kwargs) - self.code.AncillaQubit.node = None - self.Cluster.root_node = None - self.Cluster.min_delay = 0 + self.code._AncillaQubit.node = None + self._Cluster.root_node = None + self._Cluster.min_delay = 0 self.new_boundary = [] """ @@ -101,7 +99,7 @@ def cluster_add_ancilla( if new_ancilla.cluster == cluster: # cycle detected, peel edge self._edge_peel(edge, variant="cycle") else: # if no cycle detected - self.FillerNode(new_ancilla) + self._OddNode(new_ancilla) self._edge_full(ancilla, edge, new_ancilla) self.cluster_add_ancilla(cluster, new_ancilla, parent=ancilla) @@ -138,8 +136,8 @@ def find_clusters(self, **kwargs): for ancilla in plaqs + stars: if ancilla.cluster is None or ancilla.cluster.instance != self.code.instance: - node = self.SyndromeNode(ancilla) - cluster = self.Cluster(self.cluster_index, self.code.instance) + node = self._Syndrome(ancilla) + cluster = self._Cluster(self.cluster_index, self.code.instance) cluster.root_node = node self.cluster_add_ancilla(cluster, ancilla) self.cluster_index += 1 @@ -304,7 +302,7 @@ def union_bucket(self, union_list: List[Tuple[AncillaQubit, Edge, AncillaQubit]] root_node, parent, child = new_cluster.root_node, new_node, node if not node.radius % 2 and new_node.radius > 1: # Connect via new junction-node - junction = self.JunctionNode(new_ancilla) + junction = self._Junction(new_ancilla) new_ancilla.node = junction parent_edge, child_edge = parent.radius // 2, child.radius // 2 junction.neighbors = [(parent, parent_edge), (child, child_edge)] diff --git a/opensurfacesim/decoders/unionfind/__init__.py b/opensurfacesim/decoders/unionfind/__init__.py index 8fbee6cc..aecfa7cb 100644 --- a/opensurfacesim/decoders/unionfind/__init__.py +++ b/opensurfacesim/decoders/unionfind/__init__.py @@ -1,7 +1,7 @@ """ The Union-Find decoder [delfosse2017almost]_ maps each element of the syndrome :math:`\sigma` to an ancilla :math:`v` in a non-connected graph defined on the code lattice. From this starting point, it grows clusters around these ancillas by repeatedly adding a layer of edges and ancillas to existing clusters, until all clusters have an even number of non-trivial syndrome ancillas. Then, it selects a spanning tree :math:`F` for each cluster. -The leaves of each spanning tree are conditionally peeled in a tail-recursive breadth-first search until all non-trivial syndrome ancillas are paired and linked by a path within :math:`F`, which is the correcting operator :math:`\mathcal{C}` [delfosse2017linear]_. The strategy for constructing the clusters turns out to have a strong effect on performance. For instance, the threshold for bit-flip noise of a decoder that grows the clusters following a random order is 9.2% [delfosse2017almost]_, while if the clusters are grown in order of cluster size, which we call **Weighted Growth**, the threshold increases to 9.9% [delfosse2017almost]_. +The leaves of each spanning tree are conditionally peeled in a tail-recursive breadth-first search until all non-trivial syndrome ancillas are paired and linked by a path within :math:`F`, which is the correcting operator :math:`\mathcal{C}` [delfosse2017linear]_. The strategy for constructing the clusters turns out to have a strong effect on performance. For instance, the threshold for bitflip noise of a decoder that grows the clusters following a random order is 9.2% [delfosse2017almost]_, while if the clusters are grown in order of cluster size, which we call **Weighted Growth**, the threshold increases to 9.9% [delfosse2017almost]_. The complexity of the Union-Find decoder is driven by the merging of the clusters. For this, the algorithm uses the Union-Find or disjoint-set data structure [tarjan1975efficiency]_. This data structure contains a set of elements, in this case ancillas on the lattice. The set of elements is represented by a two-level tree. At the root of the tree sits one element chosen arbitrarily; the rest of the elements are linked to the root element. The structure admits two functions: :math:`Find` and :math:`Union`. Given :math:`v` an element from the structure, the function :math:`Find(v)` returns the root element of the tree. This is is used to identify the cluster to which :math:`v` belongs. The second function is :math:`Union(u, v)`, this function merges the sets associated with elements :math:`u` and :math:`v`. This requires pointing all the elements of one of the sets to the root of the other. In order to minimize the number of operations the root of the set with the larger number of elements is chosen as root for the merged set, this is called **Weighted Union**. In this context, :math:`Union` is used when the growth of a cluster requires adding a vertex that belongs to another. """ diff --git a/opensurfacesim/decoders/unionfind/plot.py b/opensurfacesim/decoders/unionfind/plot.py index a1c8b9a9..950b5f2f 100644 --- a/opensurfacesim/decoders/unionfind/plot.py +++ b/opensurfacesim/decoders/unionfind/plot.py @@ -1,15 +1,15 @@ from ...codes.elements import AncillaQubit, DataQubit, Edge from ...plot import Template2D, Template3D -from .._template import PlotCode +from .._template import Plot from .sim import Toric as SimToric, Planar as SimPlanar from matplotlib.patches import Rectangle from matplotlib.lines import Line2D -class Toric(SimToric, PlotCode): +class Toric(SimToric, Plot): """Union-Find decoder for the toric lattice with union-find plot. - Has all class attributes and methods from `.unionfind.sim.Toric`, with additional parameters below. Default values for these parameters can be supplied via a *decoders.ini* file under the section of ``[unionfind]``. + Has all class attributes and methods from `.unionfind.sim.Toric`, with additional parameters below. Default values for these parameters can be supplied via a *decoders.ini* file under the section of ``[unionfind]`` (see `.decoders._template.read_config`). The plotting class initiates a `opensurfacesim.plot` object. For its usage, see :ref:`plot-usage`. @@ -126,10 +126,16 @@ def _edge_peel(self, edge, variant="", **kwargs): return ret class Figure2D(Template2D): + """Visualizer for the Union-Find decoder and Union-Find based decoders with perfect measurements. + + Parameters + ---------- + args, kwargs + Positional and keyword arguments are forwarded to `.plot.Template2D`. + """ def __init__(self, decoder, name, *args, **kwargs) -> None: self.decoder = decoder self.code = decoder.code - self.decoder = name super().__init__(*args, **kwargs) self.colors1 = {"x": self.params.color_x_primary, "z": self.params.color_z_primary} @@ -184,6 +190,7 @@ def _plot_half_edge( instance: float, full: bool = False, ): + """Adds a line corresponding to a half-edge to the figure.""" line = self._draw_line( self.code._parse_boundary_coordinates(self.code.size[0], edge.qubit.loc[0], ancilla.loc[0]), self.code._parse_boundary_coordinates(self.code.size[0], edge.qubit.loc[1], ancilla.loc[1]), @@ -202,19 +209,23 @@ def _plot_half_edge( self.new_artist(line) - def _plot_full_edge(self, edge, ancilla): + def _plot_full_edge(self, edge: Edge, ancilla: AncillaQubit): + """Updates the line styles of the plot of an edge with support 2.""" self.new_properties(edge.uf_plot[ancilla], {"ls": self.params.line_style_primary}) - def _hide_edge(self, edge): + def _hide_edge(self, edge: Edge): + """Hides the plot of an edge after a peel or detected cycle.""" if hasattr(edge, "uf_plot"): for artist in edge.uf_plot.values(): self.new_properties(artist, {"visible": False}) - def _match_edge(self, edge): + def _match_edge(self, edge:Edge): + """Updates the color of an matched edge.""" for artist in edge.uf_plot.values(): self.new_properties(artist, {"color": self.colors1[edge.state_type]}) - def _plot_ancilla(self, ancilla, init=False): + def _plot_ancilla(self, ancilla:AncillaQubit, init:bool=False): + """Adds a syndrome to the plot.""" rotations = {"x": 0, "z": 45} @@ -242,7 +253,8 @@ def _plot_ancilla(self, ancilla, init=False): if not init: self.new_artist(ancilla.uf_plot) - def _flip_ancilla(self, ancilla): + def _flip_ancilla(self, ancilla: AncillaQubit): + """Flips the state of the ancilla on the figure.""" if ancilla.syndrome: if hasattr(ancilla, "uf_plot"): self.new_properties( @@ -269,6 +281,13 @@ def _pick_handler(self, event): print(obj) class Figure3D(Template3D, Figure2D): + """Visualizer for the Union-Find decoder and Union-Find based decoders with faulty measurements. + + Parameters + ---------- + args, kwargs + Positional and keyword arguments are forwarded to `~.decoders.unionfind.plot.Toric.Figure2D` and `.plot.Template3D`. + """ def _plot_half_edge( self, edge: Edge, @@ -276,6 +295,7 @@ def _plot_half_edge( instance: float, full: bool = False, ): + """Adds a line corresponding to a half-edge to the figure.""" if type(edge.qubit) is DataQubit: edge_z = edge.qubit.z @@ -307,7 +327,7 @@ def _plot_half_edge( class Planar(Toric, SimPlanar): """Union-Find decoder for the planar lattice with union-find plot. - Has all class attributes and methods from `.unionfind.sim.Planar`, with additional parameters below. Default values for these parameters can be supplied via a *decoders.ini* file under the section of ``[unionfind]``. + Has all class attributes and methods from `.unionfind.sim.Planar`, with additional parameters below. Default values for these parameters can be supplied via a *decoders.ini* file under the section of ``[unionfind]`` (see `.decoders._template.read_config`). The plotting class initiates a `opensurfacesim.plot` object. For its usage, see :ref:`plot-usage`. @@ -326,10 +346,18 @@ class Planar(Toric, SimPlanar): """ def init_plot(self, **kwargs): + # Inherited docstring size = [xy - 0.5 for xy in self.code.size] self._init_axis([-0.25, -0.25] + size, title=self.decoder, aspect="equal") class Figure2D(Toric.Figure2D): + """Visualizer for the Union-Find decoder and Union-Find based decoders with perfect measurements. + + Parameters + ---------- + args, kwargs + Positional and keyword arguments are forwarded to `.unionfind.plot.Toric.Figure2D`. + """ def _plot_ancilla(self, ancilla, **kwargs): if type(ancilla) == AncillaQubit: super()._plot_ancilla(ancilla, **kwargs) diff --git a/opensurfacesim/decoders/unionfind/sim.py b/opensurfacesim/decoders/unionfind/sim.py index e9cb5ed1..287cdfef 100644 --- a/opensurfacesim/decoders/unionfind/sim.py +++ b/opensurfacesim/decoders/unionfind/sim.py @@ -2,11 +2,11 @@ from typing import List, Optional, Tuple from ...codes.elements import AncillaQubit, Edge, PseudoQubit from .elements import Cluster -from .._template import SimCode +from .._template import Sim from collections import defaultdict -class Toric(SimCode): +class Toric(Sim): """Union-Find decoder for the toric lattice. In this implementation, cluster properties are not stored at the root of the tree. Instead, ancillas are collected within `~.unionfind.elements.Cluster` objects, which contain the `~.unionfind.elements.Cluster.union` and `~.unionfind.elements.Cluster.find` methods. @@ -27,7 +27,7 @@ class Toric(SimCode): print_steps : bool, optional Prints additional decoding information. Default is false. kwargs - Keyword arguments are forwarded to `~.decoders._template.SimCode`. + Keyword arguments are forwarded to `~.decoders._template.Sim`. Attributes ---------- @@ -57,7 +57,7 @@ class Toric(SimCode): name = "Union-Find" short = "unionfind" - Cluster = Cluster + _Cluster = Cluster compatibility_measurements = dict( PerfectMeasurements=True, @@ -74,11 +74,11 @@ def __init__(self, *args, **kwargs) -> None: self.config["step_growth"] = not (self.config["step_bucket"] or self.config["step_cluster"]) # Apply Monkey Patching - self.code.AncillaQubit.cluster = None - self.code.AncillaQubit.peeled = None + self.code._AncillaQubit.cluster = None + self.code._AncillaQubit.peeled = None if not self.config["dynamic_forest"]: - self.code.AncillaQubit.forest = None - self.code.edge.forest = None + self.code._AncillaQubit.forest = None + self.code._Edge.forest = None # Initiated support table self.support = {} @@ -90,7 +90,7 @@ def __init__(self, *args, **kwargs) -> None: for layer in self.code.ancilla_qubits.values(): for ancilla_qubit in layer.values(): self.support[ - ancilla_qubit.vertical_edges[ + ancilla_qubit.z_neighbors[ self.code.ancilla_qubits[(ancilla_qubit.z + 1) % self.code.layers][ancilla_qubit.loc] ] ] = 0 @@ -214,7 +214,7 @@ def find_clusters(self, **kwargs): plaqs, stars = self.get_syndrome() for ancilla in plaqs + stars: if ancilla.cluster is None or ancilla.cluster.instance != self.code.instance: - cluster = self.Cluster(self.cluster_index, self.code.instance) + cluster = self._Cluster(self.cluster_index, self.code.instance) self.cluster_add_ancilla(cluster, ancilla) self.cluster_index += 1 self.clusters.append(cluster) diff --git a/opensurfacesim/errors/_template.py b/opensurfacesim/errors/_template.py index 00cb885f..147b6ad4 100644 --- a/opensurfacesim/errors/_template.py +++ b/opensurfacesim/errors/_template.py @@ -8,9 +8,11 @@ class Sim(ABC): """Template simulation class for errors. + The template simulation error class can be used as a parent class for error modules for surface code classes that inherit from `.codes._template.sim.PerfectMeasurements` or `.codes._template.sim.FaultyMeasurements`. The error of the module must be applied to each qubit separately using the abstract method `random_error`. + Parameters ---------- - code : `~.decoders._template.SimCode` + code : `.codes._template.sim.PerfectMeasurements` Simulation surface code class. Attributes @@ -42,36 +44,118 @@ def random_error(self, qubit: Qubit, **kwargs) -> None: class Plot(Sim): """Template plot class for errors. - .. todo:: This documentation is out of date + The template plotting error class can be used as a parent class for error modules for surface code classes that inherit from `.codes._template.plot.PerfectMeasurements` or `.codes._template.plot.FaultyMeasurements`, which have a figure object attribute at ``code.figure``. The error of the module must be applied to each qubit separately using the abstract method `random_error`. + + To change properties of the qubit (a `matplotlib.patches.Circle` object) if an error has been appied to visualize the error. The template plot error class features an easy way to define the plot properties of an error. First of all, each error must be defined in an *error method* that applies the error to the qubit. The template can contain multiple *error methods*, all of which must be called by `random_error`. For all errors that we wish to plot, we must add the names of the methods to ``self.error_methods``. The plot properties are stored under the same name in ``self.plot_params``. + + .. code-block:: python + + class CustomPlotError(Plot): + + error_methods = ["example_method"] + plot_params = { + "example_method": {"edgecolor": "color_edge", "facecolor": (0,0,0,0)} + } + + def random_error(self, qubit): + if random.random < 0.5: + self.error_method(qubit) + + def example_method(self, qubit): + # apply error + pass + + Note that the properties can either be literal or refer to some attribute of the `~.plot.PlotParams` object stored at ``self.code.figure.params`` (see `~.plot.PlotParams.load_params`). Thus the name for the error methods **must be unique** to any attribute in `~plot.PlotParams`. + + Similarly, additional legend items can be added to the surface code plot ``self.code.figure``. Each legend item is a ``matplotlib.lines.line2D``. The properties for each additional item in the legend is stored at ``self.legend_params``, and must also be unique to any `~.plot.PlotParams` attribute. The legend titles for each item is stored in ``self.legend_titles`` at the same keys. The additional legend items are added in `~.codes._template.PerfectMeasurements.Figure.init_legend`. + + .. code-block:: python + + class CustomPlotError(Plot): + + error_methods = ["example_method"] + plot_params = { + "example_method": {"edgecolor": "color_edge", "facecolor": (0,0,0,0)} + } + legend_params = { + "example_item": { + "marker": "o", + "color": "color_edge", + "mfc": (1, 0, 0), + "mec": "g", + }, + } + legend_titles = { + "example_item": "Example error" + } + + def random_error(self, qubit): + if random.random < 0.5: + self.error_method(qubit) + + def example_method(self, qubit): + # apply error + pass + + Finally, error methods can be also be added to the GUI of the surface code plot. For this, each error method must a *static method* that is not dependant on the error class. Each error method to be added in the GUI must be included in ``self.gui_methods``. The GUI elements are included in `~.codes._template.PerfectMeasurements.Figure.init_plot`. + + .. code-block:: python + + class CustomPlotError(Plot): + + error_methods = ["example_method"] + gui_methods = ["example_method"] + plot_params = { + "example_method": {"edgecolor": "color_edge", "facecolor": (0,0,0,0)} + } + legend_params = { + "example_item": { + "marker": "o", + "color": "color_edge", + "mfc": (1, 0, 0), + "mec": "g", + }, + } + legend_titles = { + "example_item": "Example error" + } + + def random_error(self, qubit): + if random.random < 0.5: + self.error_method(qubit) + + @staticmethod + def example_method(qubit): + # apply error + pass Parameters ---------- - code : `~.decoders._template.PlotCode` + code : `~.codes._template.plot.PerfectMeasurements` Plotting surface code class. Attributes ---------- - legend_params, plot_params - Additional plotting parameters loaded to the `.plot.PlotParams` instance at ``self.params``. - legend_names : dict - Titles to display for the legend items in ``legend_params``. - error_methods : dict - Dictionary of error methods. Used by :meth:`opensurfacesim.code._template.plot.PerfectMeasurements.Figure._pickhandler` to apply the error directly on the figure. Each method must be of the following form. - - def error_method(qubit, rate=0): - pass - - plot_properties : dict of dict - Dictionary of plot properties for each specific error class, defined in 'plot_errors.ini'. - legend_properties : dict of dict - Dictionary of `matplotlib.lines.Line2D` properties for each legend item, defined in 'plot_errors_legend.ini'. + error_methods : list + List of names of the error methods that changes the qubit surface code plot according to properties defined in ``self.plot_params``. + plot_params : {method_name: properties} + Qubit plot properties to apply for each of the error methods in ``self.error_methods``. Properties are loaded to the `~.plot.PlotParams` object stored at the ``self.code.figure.params`` attribute of the surface code plot (see `~.plot.PlotParams.load_params`). + legend_params {method_name: Line2D properties} + Legend items to add to the surface code plot. Properties are loaded to the `~.plot.PlotParams` object stored at the ``self.code.figure.params`` attribute of the surface code plot (see `~.plot.PlotParams.load_params`), and used to initialize a `~matplotlib.lines.Line2D` legend item. + legend_titles : {method_name: legend_title} + Titles to display for the legend items in ``self.legend_params``. + gui_permanent : bool + If enabled, the application of an error method on a qubit cannot be reversed within the same simulation instance. + gui_methods : list + List of names of the static error methods include in the surface plot GUI. """ - - permanent_on_click = False - error_methods = [] - legend_params = {} - legend_names = {} - plot_params = {} + error_methods: list = [] + legend_params: dict = {} + legend_titles: dict = {} + plot_params: dict = {} + gui_permanent: bool = False + gui_methods: list = [] + def __init__(self, *args, **kwargs) -> None: super().__init__(*args, *kwargs) @@ -84,6 +168,15 @@ def __init__(self, *args, **kwargs) -> None: setattr(self, error_name, self.plot_error(error_name)) def plot_error(self, error_name): + """Decorates the error method with plotting features. + + The method ``error_name`` is decorated with plot property changes defined in ``self.plot_params``. For each of the properties to change, the original property value of the artist is stored and requested as a change at the end of the simulation instance. + + See Also + -------- + `.plot.Template2D.temporary_properties` + `.plot.Template2D.new_properties`. + """ sim_method = getattr(self, error_name) figure = self.code.figure @@ -105,7 +198,7 @@ def wrapped_method(qubit: Qubit, temporary: bool = False, **kwargs): future_properties = figure.future_dict[figure.history_iter + 3] if temporary: - if self.permanent_on_click or qubit.errors[error_name] == self.code.instance: + if self.gui_permanent or qubit.errors[error_name] == self.code.instance: figure.temporary_properties(artist, properties) if artist in future_properties: future_properties[artist].update(restored_properties) diff --git a/opensurfacesim/errors/erasure.py b/opensurfacesim/errors/erasure.py index bdfa3a6f..8d8eedea 100644 --- a/opensurfacesim/errors/erasure.py +++ b/opensurfacesim/errors/erasure.py @@ -9,16 +9,18 @@ class Sim(TemplateSim): Parameters ---------- - p_erasure : float or int, optional + p_erasure Default probability of erasure errors. + initial_states + Default state of the qubit after re-initialization. """ def __init__(self, *args, p_erasure: float = 0, initial_states: Tuple[float, float] = (0, 0), **kwargs): super().__init__(*args, **kwargs) self.initial_states = initial_states self.default_error_rates = {"p_erasure": p_erasure} - self.code.DataQubit.erasure = None - self.code.AncillaQubit.erasure = None + self.code._DataQubit.erasure = None + self.code._AncillaQubit.erasure = None # TODO above line is required for unionfind/ufns decoder, but doesn't make sense def random_error(self, qubit, p_erasure: float = 0, initial_states: Optional[Tuple[float, float]] = None, **kwargs): @@ -26,14 +28,12 @@ def random_error(self, qubit, p_erasure: float = 0, initial_states: Optional[Tup Parameters ---------- - qubit : DataQubit + qubit Qubit on which the error is (conditionally) applied. - p_erasure : float or int, optional + p_erasure Overriding probability of erasure errors. - - See Also - -------- - DataQubit + initial_states + Overriding state of the qubit after re-initialization. """ if p_erasure is None: p_erasure = self.default_error_rates["p_erasure"] @@ -44,7 +44,17 @@ def random_error(self, qubit, p_erasure: float = 0, initial_states: Optional[Tup @staticmethod def erasure(qubit: DataQubit, instance: float = 0, initial_states: Tuple[float, float] = (0, 0), **kwargs): - """Erases the `qubit` by resetting its attributes. """ + """Erases the ``qubit`` by resetting its attributes. + + Parameters + ---------- + qubit + Qubit to erase. + instance + Current simulation instance. + initial_states + State of the qubit after re-initialization. + """ qubit.erasure = instance qubit._reinitialize(initial_states=initial_states, **kwargs) @@ -66,6 +76,6 @@ class Plot(TemplatePlot, Sim): } } - legend_names = {"legend_erasure": "Erasure"} + legend_titles = {"legend_erasure": "Erasure"} plot_params = {"erasure": {"linestyle": "line_style_tertiary"}} diff --git a/opensurfacesim/errors/pauli.py b/opensurfacesim/errors/pauli.py index 8f71d4f4..4e2e48f4 100644 --- a/opensurfacesim/errors/pauli.py +++ b/opensurfacesim/errors/pauli.py @@ -10,9 +10,9 @@ class Sim(TemplateSim): Parameters ---------- p_bitflip : float or int, optional - Default probability of X-errors or bit-flip errors. + Default probability of X-errors or bitflip errors. p_phaseflip : float or int, optional - Default probability of Z-errors or phase-flip errors. + Default probability of Z-errors or phaseflip errors. """ def __init__(self, *args, p_bitflip: float = 0, p_phaseflip: float = 0, **kwargs) -> None: @@ -20,18 +20,17 @@ def __init__(self, *args, p_bitflip: float = 0, p_phaseflip: float = 0, **kwargs self.default_error_rates = {"p_bitflip": p_bitflip, "p_phaseflip": p_phaseflip} def random_error(self, qubit: Qubit, p_bitflip: float = 0, p_phaseflip: float = 0, **kwargs): - """Applies a Pauli error, bit-flip and/or phase-flip. + """Applies a Pauli error, bitflip and/or phaseflip. Parameters ---------- - p_bitflip : float or int, optional - Overriding probability of X-errors or bit-flip errors. - p_phaseflip : float or int, optional - Overriding probability of Z-errors or phase-flip errors. - - See Also - -------- - DataQubit + qubit + Qubit on which the error is (conditionally) applied. + p_bitflip + Overriding probability of X-errors or bitflip errors. + p_phaseflip + Overriding probability of Z-errors or phaseflip errors. + """ if p_bitflip is None: p_bitflip = self.default_error_rates["p_bitflip"] @@ -60,6 +59,7 @@ def phaseflip(qubit: Qubit, **kwargs): @staticmethod def bitphaseflip(qubit: Qubit, **kwargs): + """Applies a bitflip and phaseflip or ZX on ``qubit``.""" qubit.edges["x"].state = not qubit.edges["x"].state qubit.edges["z"].state = not qubit.edges["z"].state diff --git a/opensurfacesim/main.py b/opensurfacesim/main.py index aee8ac97..fc16f70f 100644 --- a/opensurfacesim/main.py +++ b/opensurfacesim/main.py @@ -1,8 +1,5 @@ """ -Contains methods to run a simulated lattice of the surface code. -The graph type (2D/3D) and decoder (MWPM, unionfind...) are specified and are loaded. -One can choose to run a simulated lattice for a single, multiple or many (multithreaded) multiple iterations. - +Contains functions and classes to run and benchmark surface code simulations and visualizations. Use `initialize` to prepare a surface code and a decoder instance, which can be passed on to `run` and `run_multiprocess` to simulate errors and to decode them with the decoder. """ from __future__ import annotations from types import ModuleType @@ -22,7 +19,7 @@ size_type = Union[Tuple[int, int], int] errors_type = List[Union[str, Error]] code_type = codes._template.sim.PerfectMeasurements -decoder_type = decoders._template.SimCode +decoder_type = decoders._template.Sim def initialize( @@ -36,6 +33,8 @@ def initialize( ): """Initializes a code and a decoder. + The function makes sure that the correct class is used to instance the surface code and decoder based on the arguments provided. A code instance must be initalized with ``enabled_errors`` by `~codes._template.sim.initialize` after class instance to make sure that plot parameters are properly loaded before loading the plotting items included in each included error module, if ``plotting`` is enabled. See `.plot.Template2D` and `.errors._template.Plot` for more information. + Parameters ---------- size @@ -51,7 +50,7 @@ def initialize( plotting Enable plotting for the surface code and/or decoder. kwargs - Keyword arguments are passed on to the chosen code and decoder. + Keyword arguments are passed on to the chosen code, `~.codes._template.sim.PerfectMeasurements.initialize`, and the chosen decoder. Examples -------- @@ -60,6 +59,21 @@ def initialize( >>> initialize((6,6), "toric", "mwpm", enabled_errors=["pauli"], check_compatibility=True) (, ) ✅ This decoder is compatible with the code. + + Keyword arguments for the code and decoder classes can be included for further customization of class initialization. Note that default errors rates for error class initialization (see `~.codes._template.sim.PerfectMeasurements.init_errors` and `.errors._template.Sim`) can also be provided as keyword arguments here. + + >>> enabled_errors = ["pauli"] + >>> code_kwargs = { + ... "initial_states": (0,0), + ... "p_bitflip": 0.1, + ... } + >>> decoder_kwargs = { + ... "check_compatibility": True, + ... "weighted_union": False, + ... "weighted_growth": False, + ... } + >>> initialize((6,6), "toric", "unionfind", enabled_errors=enabled_errors, **code_kwargs, **decoder_kwargs) + ✅ This decoder is compatible with the code. """ if isinstance(Code, str): Code = getattr(codes, Code) @@ -105,15 +119,15 @@ def run( iterations Number of iterations to run. error_rates - Dictionary for error rates (see `~opensurfacesim.errors`). + Dictionary of error rates (see `~opensurfacesim.errors`). Errors must have been loaded during code class initialization by `~.codes._template.sim.PerfectMeasurements.initialize` or `~.codes._template.sim.PerfectMeasurements.init_errors`. decode_initial - Decode initial code configuration before applying loaded errors. + Decode initial code configuration before applying loaded errors. If random states are used for the data-qubits of the ``code`` at class initialization (default behavior), an initial round of decoding is required and is enabled through the ``decode_initial`` flag (default is enabled). seed Float to use as the seed for the random number generator. benchmark Benchmarks decoder performance and analytics if attached. kwargs - Keyword arguments are passed on to ``decoder.decode()``. + Keyword arguments are passed on to `~.decoders._template.Sim.decode`. Examples -------- @@ -123,8 +137,9 @@ def run( >>> run(code, decoder, iterations=10, error_rates = {"p_bitflip": 0.1}) {'no_error': 8} - Benchmarked results are updated to the returned dictionary. See BenchmarkDecoder for the syntax and information to setup benchmarking. + Benchmarked results are updated to the returned dictionary. See `.BenchmarkDecoder` for the syntax and information to setup benchmarking. + >>> code, decoder = initialize((6,6), "toric", "mwpm", enabled_errors=["pauli"]) >>> benchmarker = BenchmarkDecoder({"decode":"duration"}) >>> run(code, decoder, iterations=10, error_rates = {"p_bitflip": 0.1}, benchmark=benchmarker) {'no_error': 8, @@ -189,9 +204,9 @@ def run_multiprocess( ): """Runs surface code simulation using multiple processes. - Using the standard module `multiprocessing` and its `~multiprocessing.Process` class, several processes are created that each runs its on contained simulation using `run`. The ``code`` and ``decoder`` objects are copied such that each process has its own instance. The total number of ``iterations`` are divided for the number of ``processes`` indicated. If no ``processes`` parameter is supplied, the number of available threads is determined via `~multiprocessing.cpu_count` and all threads are utilized. + Using the standard module `.multiprocessing` and its `~multiprocessing.Process` class, several processes are created that each runs its on contained simulation using `run`. The ``code`` and ``decoder`` objects are copied such that each process has its own instance. The total number of ``iterations`` are divided for the number of ``processes`` indicated. If no ``processes`` parameter is supplied, the number of available threads is determined via `~multiprocessing.cpu_count` and all threads are utilized. - If a `~BenchmarkDecoder` object is attached, `~multiprocessing.Process` copies the object for each separate thread. Each instance of the the decoder thus have its own benchmark object. The results of the benchmark are appended to a list and addded to the output. + If a `.BenchmarkDecoder` object is attached to ``benchmark``, `~multiprocessing.Process` copies the object for each separate thread. Each instance of the the decoder thus have its own benchmark object. The results of the benchmark are appended to a list and addded to the output. See `run` for examples on running a simulation. @@ -336,7 +351,7 @@ class BenchmarkDecoder(object): >>> benchmarker.lists {'duration': {'decode': [0.0009881999976641964]}} - The benchmark class can also be attached to run. The mean and standard deviations of the benchmarked values are in that case updated to the output of run after running ``lists_mean_var``. + The benchmark class can also be attached to run. The mean and standard deviations of the benchmarked values are in that case updated to the output of run after running `lists_mean_var`. >>> benchmarker = BenchmarkDecoder({"decode":"duration"}) >>> run(code, decoder, iterations=10, error_rates = {"p_bitflip": 0.1}, benchmark=benchmarker) diff --git a/opensurfacesim/plot.py b/opensurfacesim/plot.py index 0c968da1..8bddb411 100644 --- a/opensurfacesim/plot.py +++ b/opensurfacesim/plot.py @@ -139,6 +139,8 @@ def load_params(self, param_dict): {"new_attr" : "some_value", "use_existing" : 0.35} """ for attribute, value in param_dict.items(): + if hasattr(self, attribute): + print(f"Warning, attribute {attribute} already defined.") if isinstance(value, dict): for sub_attribute, sub_value in value.items(): if isinstance(sub_value, str): diff --git a/opensurfacesim/threshold.py b/opensurfacesim/threshold.py index 8ee9f2d0..88a908f6 100644 --- a/opensurfacesim/threshold.py +++ b/opensurfacesim/threshold.py @@ -1,4 +1,3 @@ -from collections import defaultdict from typing import Dict, List, Tuple, Union, Optional from types import ModuleType from dataclasses import dataclass @@ -38,14 +37,14 @@ def run_many( ) -> Optional[pd.DataFrame]: """Runs a series of simulations of varying sizes and error rates. - A series of simulations are run without plotting for all combinations of ``sizes`` and ``error_rates``. The results are returned as a Pandas DataFrame and saved to the working directory as a csv file. If an existing csv file with the same file name is found, the existing file is loaded and new results are appended to the existing data. + A series of simulations are run without plotting for all combinations of ``sizes`` and ``error_rates``. The results are returned as a Pandas DataFrame and saved to the working directory as a csv file. If an existing csv file with the same file name is found, the existing file is loaded and new results are appended to the existing data. A `.main.BenchmarkDecoder` object is attached to each simulation to log the seed and other information. Parameters ---------- Code - A surface code instance (see `~.main.initialize`). - decoder - A decoder instance (see `~.main.initialize`). + Any surface code module or module name from codes. + Decoder + Any decoder module or module name from decoders iterations Number of iterations to run per configuration. sizes @@ -62,6 +61,34 @@ def run_many( File name of outputted csv data. If set to "none", no file will be saved. mp_processses Number of processes to spawn. For a single process, `~.main.run` is used. For multiple processes, `~main.run_multiprocess` is utilized. + + Examples + -------- + A series of simulations using the ``toric`` surface code and ``mwpm`` decoder can be easily setup. Benchmarking can be performed by supplying the ``methods_to_benchmark`` argument of the `~.main.BenchmarkDecoder` class. The function will initialize a benchmark object of each configuration and append all results as columns to the returned dataframe. + + >>> data = run_many( + ... "toric", + ... "mwpm", + ... iterations = 1000, + ... sizes = [8,12,16], + ... enabled_errors = ["pauli"], + ... error_rates = [{"p_bitflip: p} for p in [0.09, 0.1, 0.11]], + ... ) + >>> print(data) + datetime decoded iterations no_error p_bitflip seed size + 0 04/11/2020 14:45:36 1000.0 1000.0 820.0 0.09 13163.013981 8.0 + 1 04/11/2020 14:45:45 1000.0 1000.0 743.0 0.10 13172.277886 8.0 + 2 04/11/2020 14:45:54 1000.0 1000.0 673.0 0.11 13181.090130 8.0 + 3 04/11/2020 14:46:36 1000.0 1000.0 812.0 0.09 13190.191461 12.0 + 4 04/11/2020 14:47:18 1000.0 1000.0 768.0 0.10 13232.408302 12.0 + 5 04/11/2020 14:48:16 1000.0 1000.0 629.0 0.11 13274.044268 12.0 + 6 04/11/2020 14:51:47 1000.0 1000.0 855.0 0.09 13332.153639 16.0 + 7 04/11/2020 14:55:15 1000.0 1000.0 754.0 0.10 13542.533067 16.0 + 8 04/11/2020 14:59:14 1000.0 1000.0 621.0 0.11 13751.082511 16.0 + + + + """ sys.setrecursionlimit(recursion_limit) @@ -132,7 +159,13 @@ def read_csv(file: str) -> pd.DataFrame: @dataclass class ThresholdFit: - """Fitter for code threshold with data obtained by ``~.threshold.run``.""" + """Fitter for code threshold with data obtained by ``~.threshold.run``. + + Threshold fitting is performed using the equations described in [wang2003confinement]_. The threshold is computing the ground state of the Hamiltonian that described the phase transition or the Nishimori line in the Random Bond Ising Model. The source provides two functions which are included in this fitting class, where the ``modified_ansatz`` includes a nonuniversion additive correction to correct for finite size effects. + + .. [wang2003confinement] Chenyang Wang, Jim Harrington and John Preskill, *Confinement-Higgs transition in a disordered gauge theory and the accuracy threshold for quantum memory*, Annals of Physics, 1:31-58, 2003. + + """ modified_ansatz: bool = False p: fit_param = (0.09, 0.1, 0.11) diff --git a/setup.py b/setup.py index 420a3041..4010db64 100644 --- a/setup.py +++ b/setup.py @@ -9,11 +9,14 @@ setup( name="opensurfacesim", - version="0.1.0", + version="0.1.1", description="Open library from surface code simulations and visualizations", long_description=README, long_description_content_type="text/markdown", url="https://github.com/watermarkhu/opensurfacesim", + project_urls={ + "Documentation": "https://opensurfacesim.readthedocs.io/en/latest/" + }, author="Mark Shui Hu", author_email="watermarkhu@gmail.com", license="BSD-3", @@ -25,12 +28,13 @@ ], packages=find_packages(exclude=['tests', '*.tests', '*.tests.*']), include_package_data = True, + python_requires='>3.7.0', install_requires=[ - "matplotlib", - "networkx", - "pandas", - "scipy", - "pptree", + "matplotlib>=3.3.2", + "networkx>=2.0", + "pandas>=1.1.0", + "scipy>=1.4.0", + "pptree>=3.1", ], entry_points={ "console_scrips":[