qecsim demos¶
Simulating error correction with a toric stabilizer code¶
This demo shows verbosely how to simulate one error correction run.
qecsim.app.run_once(code, error_model, decoder, error_probability)
,qecsim.app.run(code, error_model, decoder, error_probability, max_runs, max_failures)
.Notes:
Operators can be visualised in binary symplectic form (bsf) or Pauli form, e.g.
[1 1 0|0 1 0] = XYI
.The binary symplectic product is denoted by \(\odot\) and defined as \(A \odot B \equiv A \Lambda B \bmod 2\) where \(\Lambda = \left[\begin{matrix} 0 & I \\ I & 0 \end{matrix}\right]\).
Binary addition is denoted by \(\oplus\) and defined as addition modulo 2, or equivalently exclusive-or.
Initialise the models¶
%run qsu.ipynb # color-printing functions
import numpy as np
from qecsim import paulitools as pt
from qecsim.models.generic import DepolarizingErrorModel
from qecsim.models.toric import ToricCode, ToricMWPMDecoder
# initialise models
my_code = ToricCode(5, 5)
my_error_model = DepolarizingErrorModel()
my_decoder = ToricMWPMDecoder()
# print models
print(my_code)
print(my_error_model)
print(my_decoder)
ToricCode(5, 5)
DepolarizingErrorModel()
ToricMWPMDecoder()
Generate a random error¶
# set physical error probability to 10%
error_probability = 0.1
# seed random number generator for repeatability
rng = np.random.default_rng(59)
# error: random error based on error probability
error = my_error_model.generate(my_code, error_probability, rng)
qsu.print_pauli('error:\n{}'.format(my_code.new_pauli(error)))
error:
┼─·─┼─·─┼─·─┼─·─┼─·
· · · Y Y
┼─·─┼─·─┼─Z─┼─·─┼─Y
· Z · · Z
┼─·─┼─·─┼─·─┼─·─┼─·
· · X · ·
┼─·─┼─·─┼─·─┼─·─┼─·
· · · · ·
┼─·─┼─·─┼─·─┼─·─┼─·
· · · · ·
Evaluate the syndrome¶
The syndrome is a binary array indicating the stabilizers with which the error does not commute. It is calculated as \(syndrome = error \odot stabilisers^T\).
# syndrome: stabilizers that do not commute with the error
syndrome = pt.bsp(error, my_code.stabilizers.T)
qsu.print_pauli('syndrome:\n{}'.format(my_code.ascii_art(syndrome)))
syndrome:
┼───┼───┼───X───X──
│ │ │ Z │ │
X───X───X───┼───X──
│ │ │ │ │ Z
┼───X───┼───┼───X──
│ │ Z │ Z │ │
┼───┼───┼───┼───┼──
│ │ │ │ │
┼───┼───┼───┼───┼──
│ │ │ │ │
Find a recovery operation¶
In this case, the recovery operation is found by a minimum weight perfect matching decoder that finds the recovery operation as follows:
The syndrome is resolved to plaquettes using:
ToricCode.syndrome_to_plaquette_indices
.A graph between plaquettes is built with weights given by:
ToricMWPMDecoder.distance
.A MWPM algorithm is used to match plaquettes into pairs.
A recovery operator is constructed by applying the shortest path between matching plaquette pairs using:
ToricPauli.path
.
# recovery: best match recovery operation based on decoder
recovery = my_decoder.decode(my_code, syndrome)
qsu.print_pauli('recovery:\n{}'.format(my_code.new_pauli(recovery)))
recovery:
┼─·─┼─·─┼─·─┼─Z─┼─·
· · · · ·
┼─·─┼─Z─┼─X─┼─·─┼─·
Z · · X Y
┼─Z─┼─·─┼─·─┼─·─┼─·
· · X · ·
┼─·─┼─·─┼─·─┼─·─┼─·
· · · · ·
┼─·─┼─·─┼─·─┼─·─┼─·
· · · · ·
As a sanity check, we expect \(recovery \oplus error\) to commute with all stabilizers, i.e. \((recovery \oplus error) \odot stabilisers^T = 0\).
# check recovery ^ error commutes with stabilizers (by construction)
print(pt.bsp(recovery ^ error, my_code.stabilizers.T))
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0]
Visualise \(recovery \oplus error\)¶
Just out of curiosity, we can see what \(recovery \oplus error\) looks like. If successful, it should be a product of stabilizer plaquette / vertex operators.
# print recovery ^ error (out of curiosity)
qsu.print_pauli('recovery ^ error:\n{}'.format(my_code.new_pauli(recovery ^ error)))
recovery ^ error:
┼─·─┼─·─┼─·─┼─Z─┼─·
· · · Y Y
┼─·─┼─Z─┼─Y─┼─·─┼─Y
Z Z · X X
┼─Z─┼─·─┼─·─┼─·─┼─·
· · · · ·
┼─·─┼─·─┼─·─┼─·─┼─·
· · · · ·
┼─·─┼─·─┼─·─┼─·─┼─·
· · · · ·
Test if the recovery operation is successful¶
The recovery operation is successful iff \(recovery \oplus error\) commutes with all logical operators, i.e. \((recovery \oplus error) \odot logicals^T = 0.\)
# success iff recovery ^ error commutes with logicals
print(pt.bsp(recovery ^ error, my_code.logicals.T))
[1 0 0 0]
Note: The decoder is not guaranteed to find a successful recovery operation. The toric 5 x 5 code has distance \(d = 5\) so we can only guarantee to correct errors up to weight \((d - 1)/2=2\).
Equivalent code in single call¶
The above demo is equivalent to the following code.
# repeat demo in single call
from qecsim import app
print(app.run_once(my_code, my_error_model, my_decoder, error_probability))
{'error_weight': 4, 'success': True}