Adding a new code to PanQEC
In this tutorial, we will learn how to create your own custom code using PanQEC’s template and structure, with the goal of visualizing it in the GUI and calculating its threshold.
Let’s start by making a few imports:
[1]:
from panqec.codes import StabilizerCode
from panqec.gui import GUI
Creating the code class
Let’s say you want to implement your own version of the 3D toric code. In PanQEC, a code is represented by a class that inherits from the base class StabilizerCode
. More precisely, the class should have the following structure:
[2]:
class MyToric3DCode(StabilizerCode):
dimension = 3
deformation_names = ['XZZX']
@property
def label(self):
pass
def get_qubit_coordinates(self):
pass
def get_stabilizer_coordinates(self):
pass
def stabilizer_type(self, location):
pass
def get_stabilizer(self, location):
pass
def qubit_axis(self, location):
pass
def get_logicals_x(self):
pass
def get_logicals_z(self):
pass
def get_deformation(self, location, deformation_name, **kwargs):
pass
def stabilizer_representation(self, location, rotated_picture=False):
pass
def qubit_representation(self, location, rotated_picture=False):
pass
The two class attributes represent the dimension of the code (useful mainly for the visualization) and a list of potential Clifford-deformations. For instance, we will show here how to implement the XZZX-type Clifford-deformation presented in this paper.
Let’s now consider the different class methods one-by-one.
Label
The label is a name that should uniquely identify a code and its parameters. It is used for instance to identify each code in the output generated when calculating a threshold. In our example, we can give it a name of the form 'My Toric {L_x}x{L_y}x{L_z}'
[3]:
def label(self):
return 'My Toric {}x{}x{}'.format(*self.size)
Qubit and stabilizer coordinates
The first step when implementing a new code is to define a coordinate system. This coordinate system should contain both the qubits and the stabilizers and uniquely identify them. But apart from these requirements, the user is free to choose the most convenient system for the code. For instance, if several stabilizer (or qubit) sit at a given location (e.g. for the color code), one can add an extra dimension to the coordinates in order to uniquely specify each of those (e.g. (0,x,y,z)
for
the X plaquettes and (1,x,y,z)
for the Z plaquettes in the color code). On the other hand, not all locations in the coordinate system have to be filled by qubit or stabilizers.
For our 3D toric code, the coordinate system will consist of cartesian coordinates (x,y,z)
, where horizontal qubits/faces sit at even z
(starting at z=0
), and vertical qubits/faces at odd z
.
To construct the code, PanQEC then simply takes as input the list of coordinates for both qubits and stabilizers:
[4]:
def get_qubit_coordinates(self):
coordinates = []
Lx, Ly, Lz = self.size
# Qubits along e_x
for x in range(1, 2*Lx, 2):
for y in range(0, 2*Ly, 2):
for z in range(0, 2*Lz, 2):
coordinates.append((x, y, z))
# Qubits along e_y
for x in range(0, 2*Lx, 2):
for y in range(1, 2*Ly, 2):
for z in range(0, 2*Lz, 2):
coordinates.append((x, y, z))
# Qubits along e_z
for x in range(0, 2*Lx, 2):
for y in range(0, 2*Ly, 2):
for z in range(1, 2*Lz, 2):
coordinates.append((x, y, z))
return coordinates
[5]:
def get_stabilizer_coordinates(self):
coordinates = []
Lx, Ly, Lz = self.size
# Vertices
for x in range(0, 2*Lx, 2):
for y in range(0, 2*Ly, 2):
for z in range(0, 2*Lz, 2):
coordinates.append((x, y, z))
# Face in xy plane
for x in range(1, 2*Lx, 2):
for y in range(1, 2*Ly, 2):
for z in range(0, 2*Lz, 2):
coordinates.append((x, y, z))
# Face in yz plane
for x in range(0, 2*Lx, 2):
for y in range(1, 2*Ly, 2):
for z in range(1, 2*Lz, 2):
coordinates.append((x, y, z))
# Face in xz plane
for x in range(1, 2*Lx, 2):
for y in range(0, 2*Ly, 2):
for z in range(1, 2*Lz, 2):
coordinates.append((x, y, z))
return coordinates
Defining stabilizers
The next step is to define stabilizers. For that, we first define a helper method stabilizer_type
that specifies whether a stabilizer at a given location is a vertex or a face.
[6]:
def stabilizer_type(self, location):
x, y, z = location
if x % 2 == 0 and y % 2 == 0:
return 'vertex'
else:
return 'face'
We then create a method get_stabilizer
that takes a given location and returns an operator, in the form of a dictionary that assigns a Pauli (as a string ‘X’, ‘Y’ or ‘Z’) to each qubit location in the support of the stabilizer. For instance, if we are given the location (1,1,0)
(which should correspond to a horizontal face), the method will return the following:
get_stabilizer((1,1,0)) -> {(1,0,0): 'X', (0,1,0): 'X', (2,1,0): 'X', (1,2,0): 'X'}
[7]:
def get_stabilizer(self, location):
if self.stabilizer_type(location) == 'vertex':
pauli = 'Z'
else:
pauli = 'X'
x, y, z = location
# delta specifies the positions of the qubits involved in the stabilizer
# relative to the stabilizer position
if self.stabilizer_type(location) == 'vertex':
delta = [(-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1)]
else:
# Face in xy-plane.
if z % 2 == 0:
delta = [(-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, 1, 0)]
# Face in yz-plane.
elif (x % 2 == 0):
delta = [(0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1)]
# Face in zx-plane.
elif (y % 2 == 0):
delta = [(-1, 0, 0), (1, 0, 0), (0, 0, -1), (0, 0, 1)]
operator = dict()
for d in delta:
Lx, Ly, Lz = self.size
qubit_location = ((x + d[0]) % (2*Lx), (y + d[1]) % (2*Ly), (z + d[2]) % (2*Lz))
if self.is_qubit(qubit_location):
operator[qubit_location] = pauli
return operator
Qubit axis
The method qubit_axis
takes the location of a qubit in the coordinate system and returns its orientation axis. For instance, in the 3D toric code, qubits are represented as edges, that can be oriented along the x, y or z axis. Therefore, qubit_axis
will return the string 'x'
, 'y'
or 'z'
. This method is useful for defining Clifford deformations (XZZX-type transformations of the code) along a given axis.
[8]:
def qubit_axis(self, location):
x, y, z = location
if (z % 2 == 0) and (x % 2 == 1) and (y % 2 == 0):
axis = 'x'
elif (z % 2 == 0) and (x % 2 == 0) and (y % 2 == 1):
axis = 'y'
else:
axis = 'z'
return axis
Logical operators
We now need to define the logical operators of the code. Like for the stabilizers, a logical operator is represented as a dictionary that associates a Pauli string ('X'
, 'Y'
or 'Z'
) to each qubit location. The methods get_logicals_x
and get_logicals_z
return a list of such dictionaries, whose length should be the number of logical qubits of our code.
[9]:
def get_logicals_x(self):
"""The 3 logical X operators."""
Lx, Ly, Lz = self.size
logicals = []
# X operators along x edges in x direction.
operator = dict()
for x in range(1, 2*Lx, 2):
operator[(x, 0, 0)] = 'X'
logicals.append(operator)
# X operators along y edges in y direction.
operator = dict()
for y in range(1, 2*Ly, 2):
operator[(0, y, 0)] = 'X'
logicals.append(operator)
# X operators along z edges in z direction
operator = dict()
for z in range(1, 2*Lz, 2):
operator[(0, 0, z)] = 'X'
logicals.append(operator)
return logicals
[10]:
def get_logicals_z(self):
"""Get the 3 logical Z operators."""
Lx, Ly, Lz = self.size
logicals = []
# Z operators on x edges forming surface normal to x (yz plane).
operator = dict()
for y in range(0, 2*Ly, 2):
for z in range(0, 2*Lz, 2):
operator[(1, y, z)] = 'Z'
logicals.append(operator)
# Z operators on y edges forming surface normal to y (zx plane).
operator = dict()
for z in range(0, 2*Lz, 2):
for x in range(0, 2*Lx, 2):
operator[(x, 1, z)] = 'Z'
logicals.append(operator)
# Z operators on z edges forming surface normal to z (xy plane).
operator = dict()
for x in range(0, 2*Lx, 2):
for y in range(0, 2*Ly, 2):
operator[(x, y, 1)] = 'Z'
logicals.append(operator)
return logicals
Clifford-deformations
PanQEC has been designed to handle Clifford deformations and biased noise in a simple way. The method get_deformation
takes at least two parameters: the location of the qubit and the name of the deformation. For the 3D toric code, we will consider an XZZX-type deformation where every qubit along one of the axis is Hadamared. Therefore, the method also takes a deformation_axis
parameter that indicates which axis should be Hadamared. The method then returns a dictionary with the Pauli
mapping on the current qubit. In our case, we check that the qubit location given as parameter lives on the deformation axis, and if so, we return the Hadamard mapping {'X': 'Z', 'Y': 'Y', 'Z': 'X'}
. The deformation can then be used in two way:
Through
code.deform(deformation_name, **kwargs)
function, which turns the code into its deformed version inplace. This is used in the GUI to visualize the Clifford-deformation.Through the error model. A PauliErrorModel takes as parameter
deformation_name
anddeformation_kwargs
, which are used to Clifford-deform the error model instead of the code. This is the most common approach in simulations.
[11]:
def get_deformation(self, location, deformation_name, deformation_axis='y'):
if deformation_name == 'XZZX':
undeformed_dict = {'X': 'X', 'Y': 'Y', 'Z': 'Z'}
deformed_dict = {'X': 'Z', 'Y': 'Y', 'Z': 'X'}
if self.qubit_axis(location) == deformation_axis:
deformation = deformed_dict
else:
deformation = undeformed_dict
else:
raise ValueError(f"The deformation {deformation_name}"
"does not exist")
return deformation
Qubit and stabilizer visual representation
With all those methods defined, we are ready to simulate the code. If that’s all you want to do, you can skip this section. However, if you can visualize the code in the GUI, you can spot bugs and intuititively understand what is actually going on. To visualize it, we need to tell PanQEC how to visually represent qubits and stabilizers, such as what shapes and colors to use. The way to do this is to make a JSON file that specifies all the visual parameters of the code. Here is an example of JSON file that we can use for our 3D toric code:
[12]:
{
"MyToric3DCode": {
"qubits": {
"kitaev": {
"object": "cylinder",
"color": {
"I": "pink",
"X": "red",
"Y": "green",
"Z": "blue"
},
"opacity": {
"activated": {
"min": 1,
"max": 1
},
"deactivated": {
"min": 0.1,
"max": 0.6
}
},
"params": {
"length": 2,
"radius": 0.1,
"angle": 0
}
},
"rotated": {
"object": "sphere",
"color": {
"I": "white",
"X": "red",
"Y": "green",
"Z": "blue"
},
"opacity": {
"activated": {
"min": 1,
"max": 1
},
"deactivated": {
"min": 0.1,
"max": 0.4
}
},
"params": {
"radius": 0.2
}
}
},
"stabilizers": {
"kitaev": {
"vertex": {
"object": "sphere",
"color": {
"activated": "gold",
"deactivated": "white"
},
"opacity": {
"activated": {
"min": 1,
"max": 1
},
"deactivated": {
"min": 0.1,
"max": 0.6
}
},
"params": {"radius": 0.2}
},
"face": {
"object": "rectangle",
"color": {
"activated": "gold",
"deactivated": "blue"
},
"opacity": {
"activated": {
"min": 0.6,
"max": 0.6
},
"deactivated": {
"min": 0,
"max": 0
}
},
"params": {
"w": 1.5,
"h": 1.5,
"normal": [1, 0, 0],
"angle": 0
}
}
},
"rotated": {
"vertex": {
"object": "octahedron",
"color": {
"activated": "orange",
"deactivated": "orange"
},
"opacity": {
"activated": {
"min": 0.9,
"max": 0.9
},
"deactivated": {
"min": 0.1,
"max": 0.3
}
},
"params": {
"length": 1,
"angle": 0
}
},
"face": {
"object": "rectangle",
"color": {
"activated": "gold",
"deactivated": "gold"
},
"opacity": {
"activated": {
"min": 0.9,
"max": 0.9
},
"deactivated": {
"min": 0.1,
"max": 0.3
}
},
"params": {
"w": 1.4142,
"h": 1.4142,
"normal": [0,0,1],
"angle": 0.7854
}
}
}
}
}
}
[12]:
{'MyToric3DCode': {'qubits': {'kitaev': {'object': 'cylinder',
'color': {'I': 'pink', 'X': 'red', 'Y': 'green', 'Z': 'blue'},
'opacity': {'activated': {'min': 1, 'max': 1},
'deactivated': {'min': 0.1, 'max': 0.6}},
'params': {'length': 2, 'radius': 0.1, 'angle': 0}},
'rotated': {'object': 'sphere',
'color': {'I': 'white', 'X': 'red', 'Y': 'green', 'Z': 'blue'},
'opacity': {'activated': {'min': 1, 'max': 1},
'deactivated': {'min': 0.1, 'max': 0.4}},
'params': {'radius': 0.2}}},
'stabilizers': {'kitaev': {'vertex': {'object': 'sphere',
'color': {'activated': 'gold', 'deactivated': 'white'},
'opacity': {'activated': {'min': 1, 'max': 1},
'deactivated': {'min': 0.1, 'max': 0.6}},
'params': {'radius': 0.2}},
'face': {'object': 'rectangle',
'color': {'activated': 'gold', 'deactivated': 'blue'},
'opacity': {'activated': {'min': 0.6, 'max': 0.6},
'deactivated': {'min': 0, 'max': 0}},
'params': {'w': 1.5, 'h': 1.5, 'normal': [1, 0, 0], 'angle': 0}}},
'rotated': {'vertex': {'object': 'octahedron',
'color': {'activated': 'orange', 'deactivated': 'orange'},
'opacity': {'activated': {'min': 0.9, 'max': 0.9},
'deactivated': {'min': 0.1, 'max': 0.3}},
'params': {'length': 1, 'angle': 0}},
'face': {'object': 'rectangle',
'color': {'activated': 'gold', 'deactivated': 'gold'},
'opacity': {'activated': {'min': 0.9, 'max': 0.9},
'deactivated': {'min': 0.1, 'max': 0.3}},
'params': {'w': 1.4142,
'h': 1.4142,
'normal': [0, 0, 1],
'angle': 0.7854}}}}}}
Let’s dissect the structure of this file. Since a single JSON file can describe several codes, the highest-level key simply indicates the name of the class.
The second level should contain two keys, "qubits"
and "stabilizers"
, which will respectively describe the qubits and stabilizer representations. Then, for each of those, we have to indicate two representations: one for the Kitaev picture (where qubits are usually edges) and one of the rotated picture (where qubits are usually vertices). It’s mostly useful for codes that actually have those two representations (such as the 2D and 3D toric codes), but if you only want to describe one
representation, you’re free to copy-paste the same content in both the "kitaev"
and the "rotated"
fields.
Now comes the core of the representation. For each qubit/stabilizer in the Kitaev/rotated picture, we have to specify four elements: "object"
, "color"
, "opacity"
, "params"
. The "object"
key indicates the general type of object we want. The definition of the different objects can be found in the file panqec/gui/js/shape.js of the repository, and currently contains the following possibilities:
cylinder
, sphere
, rectangle
, cube
and octahedron
.
We then have to indicate the color of each object. For the qubits, we have to specify four colors, depending on whether there is an error ("X"
, "Y"
or "Z"
) or no error ("I"
) on this qubit. For the stabilizers, we indicate a color for when it is activated (i.e. when there is an excitation) or not. The full list of color names can be found by printing the StabilizerCode
property code.colormap
. Currently, direct RBG values are not supported, but will come in subsequent
versions of PanQEC.
We can also specify the opacity of our object, in four different cases, depending on whether the stabilizer/qubit is activated or not and if we currently are at the minimum or maximum opacity (this can be changed interactively in the GUI). The opacity is a number between 0
and 1
, where 0
is completely transparent and 1
completely opaque.
Finally, we specify the parameters of the object through the key params
. Those parameters are object-dependent and can also be extracted from panqec/gui/js/shape.js. Many parameters correspond to sizes (such as the radius of a sphere or the lengths of a cube), and for those, the scale is defined following the convention of the coordinate system defined above.
Once the JSON is written (let’s say in a file called toric3d.json
, located in the same folder as your python file), we have to link it to our class. For that we override two methods qubit_representation
and stabilizer_representation
, that take a location and return the representation (as a Python dictionary) corresponding to the given qubit or stabilizer (and whose highest-level keys are object
, color
, opacity
, params
and a new key location
that we will discuss
below). We start by calling the base method using super()
, with the json file we have just created as a parameter:
[13]:
def stabilizer_representation(self, location, rotated_picture=False):
representation = super().stabilizer_representation(location, rotated_picture, json_file='toric3d.json')
return representation
def qubit_representation(self, location, rotated_picture=False):
representation = super().qubit_representation(location, rotated_picture, json_file='toric3d.json')
return representation
Those base methods will automatically parse the json file and return the dictionary in the correct format. In particular, they fill the key "location"
using the location we have given as a parameter, and for the qubits, use the method qubit_axis
to automatically orient the cylinder. However, overriding those methods can be useful not only to specify a new JSON file, but also to modify the parameters of the different objects dynamically depending on their location. In our case, we want
the orientation of the faces to depend on the position. We can therefore override the parameter normal
of the rectangle (specifying its normal axis):
[14]:
def stabilizer_representation(self, location, rotated_picture=False):
representation = super().stabilizer_representation(location, rotated_picture, json_file='toric3d.json')
x, y, z = location
if not rotated_picture and self.stabilizer_type(location) == 'face':
if z % 2 == 0: # xy plane
representation['params']['normal'] = [0, 0, 1]
elif x % 2 == 0: # yz plane
representation['params']['normal'] = [1, 0, 0]
else: # xz plane
representation['params']['normal'] = [0, 1, 0]
if rotated_picture and self.stabilizer_type(location) == 'face':
if z % 2 == 0:
representation['params']['normal'] = [0, 0, 1]
elif x % 2 == 0:
representation['params']['normal'] = [1, 0, 0]
else:
representation['params']['normal'] = [0, 1, 0]
return representation
Another common key that we could want to override is the location
parameter. Indeed, in some coordinate system, the coordinates of a qubit or stabilizer can be different from its spatial location. For instance, we previously discussed how if two qubit or two stabilizers are sitting at the same location, we can add a new coordinate to specify it uniquely. An example where this appears is the Haah code, where each site contains two qubits. Qubit coordinates are therefore specified as a 4-tuple
(i,x,y,z)
. where i
is either 0
or 1
. In this case, we need to override the location
parameter to specify the actual spatial location of each qubit:
[15]:
def qubit_representation(self, location, rotated_picture=False):
representation = super().qubit_representation(location, rotated_picture)
i, x, y, z = location
d = representation['params']['radius']
if i == 0:
representation['location'] = [x-d, y, z]
else:
representation['location'] = [x+d, y, z]
return representation
Complete class
Here is what our class now looks like:
[16]:
class MyToric3DCode(StabilizerCode):
dimension = 3
@property
def label(self):
return 'My Toric {}x{}x{}'.format(*self.size)
def get_qubit_coordinates(self):
coordinates = []
Lx, Ly, Lz = self.size
# Qubits along e_x
for x in range(1, 2*Lx, 2):
for y in range(0, 2*Ly, 2):
for z in range(0, 2*Lz, 2):
coordinates.append((x, y, z))
# Qubits along e_y
for x in range(0, 2*Lx, 2):
for y in range(1, 2*Ly, 2):
for z in range(0, 2*Lz, 2):
coordinates.append((x, y, z))
# Qubits along e_z
for x in range(0, 2*Lx, 2):
for y in range(0, 2*Ly, 2):
for z in range(1, 2*Lz, 2):
coordinates.append((x, y, z))
return coordinates
def get_stabilizer_coordinates(self):
coordinates = []
Lx, Ly, Lz = self.size
# Vertices
for x in range(0, 2*Lx, 2):
for y in range(0, 2*Ly, 2):
for z in range(0, 2*Lz, 2):
coordinates.append((x, y, z))
# Face in xy plane
for x in range(1, 2*Lx, 2):
for y in range(1, 2*Ly, 2):
for z in range(0, 2*Lz, 2):
coordinates.append((x, y, z))
# Face in yz plane
for x in range(0, 2*Lx, 2):
for y in range(1, 2*Ly, 2):
for z in range(1, 2*Lz, 2):
coordinates.append((x, y, z))
# Face in xz plane
for x in range(1, 2*Lx, 2):
for y in range(0, 2*Ly, 2):
for z in range(1, 2*Lz, 2):
coordinates.append((x, y, z))
return coordinates
def stabilizer_type(self, location):
x, y, z = location
if x % 2 == 0 and y % 2 == 0:
return 'vertex'
else:
return 'face'
def get_stabilizer(self, location, deformed_axis=None):
if self.stabilizer_type(location) == 'vertex':
pauli = 'Z'
else:
pauli = 'X'
x, y, z = location
if self.stabilizer_type(location) == 'vertex':
delta = [(-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1)]
else:
# Face in xy-plane.
if z % 2 == 0:
delta = [(-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, 1, 0)]
# Face in yz-plane.
elif (x % 2 == 0):
delta = [(0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1)]
# Face in zx-plane.
elif (y % 2 == 0):
delta = [(-1, 0, 0), (1, 0, 0), (0, 0, -1), (0, 0, 1)]
operator = dict()
for d in delta:
Lx, Ly, Lz = self.size
qubit_location = ((x + d[0]) % (2*Lx), (y + d[1]) % (2*Ly), (z + d[2]) % (2*Lz))
if self.is_qubit(qubit_location):
operator[qubit_location] = pauli
return operator
def qubit_axis(self, location):
x, y, z = location
if (z % 2 == 0) and (x % 2 == 1) and (y % 2 == 0):
axis = 'x'
elif (z % 2 == 0) and (x % 2 == 0) and (y % 2 == 1):
axis = 'y'
else:
axis = 'z'
return axis
def get_logicals_x(self):
Lx, Ly, Lz = self.size
logicals = []
# X operators along x edges in x direction.
operator = dict()
for x in range(1, 2*Lx, 2):
operator[(x, 0, 0)] = 'X'
logicals.append(operator)
# X operators along y edges in y direction.
operator = dict()
for y in range(1, 2*Ly, 2):
operator[(0, y, 0)] = 'X'
logicals.append(operator)
# X operators along z edges in z direction
operator = dict()
for z in range(1, 2*Lz, 2):
operator[(0, 0, z)] = 'X'
logicals.append(operator)
return logicals
def get_logicals_z(self):
Lx, Ly, Lz = self.size
logicals = []
# Z operators on x edges forming surface normal to x (yz plane).
operator = dict()
for y in range(0, 2*Ly, 2):
for z in range(0, 2*Lz, 2):
operator[(1, y, z)] = 'Z'
logicals.append(operator)
# Z operators on y edges forming surface normal to y (zx plane).
operator = dict()
for z in range(0, 2*Lz, 2):
for x in range(0, 2*Lx, 2):
operator[(x, 1, z)] = 'Z'
logicals.append(operator)
# Z operators on z edges forming surface normal to z (xy plane).
operator = dict()
for x in range(0, 2*Lx, 2):
for y in range(0, 2*Ly, 2):
operator[(x, y, 1)] = 'Z'
logicals.append(operator)
return logicals
def stabilizer_representation(self, location, rotated_picture=False):
representation = super().stabilizer_representation(location, rotated_picture, json_file='toric3d.json')
x, y, z = location
if not rotated_picture and self.stabilizer_type(location) == 'face':
if z % 2 == 0: # xy plane
representation['params']['normal'] = [0, 0, 1]
elif x % 2 == 0: # yz plane
representation['params']['normal'] = [1, 0, 0]
else: # xz plane
representation['params']['normal'] = [0, 1, 0]
if rotated_picture and self.stabilizer_type(location) == 'face':
if z % 2 == 0:
representation['params']['normal'] = [0, 0, 1]
elif x % 2 == 0:
representation['params']['normal'] = [1, 0, 0]
else:
representation['params']['normal'] = [0, 1, 0]
return representation
def qubit_representation(self, location, rotated_picture=False):
representation = super().qubit_representation(location, rotated_picture, json_file='toric3d.json')
return representation
def get_deformation(self, location, deformation_name, deformation_axis='y'):
if deformation_name == 'XZZX':
undeformed_dict = {'X': 'X', 'Y': 'Y', 'Z': 'Z'}
deformed_dict = {'X': 'Z', 'Y': 'Y', 'Z': 'X'}
if self.qubit_axis(location) == deformation_axis:
deformation = deformed_dict
else:
deformation = undeformed_dict
else:
raise ValueError(f"The deformation {deformation_name}"
"does not exist")
return deformation
Visualizing the code in the GUI
Now that the code has been implemented, we can add it to the GUI in order to visualize it.
gui = GUI()
gui.add_code(MyToric3DCode, 'My Toric 3D')
gui.run(port=5000)
This code should start a server at the address http://127.0.0.1:5000. To see your code, open the address on your favorite web browser, click on “3D codes”, and select “My Toric 3D” as a code in the contextual menu on the right. Congratulations, you have added a new code in the GUI!
Once you’ve made sure that the code is correct by testing it on the GUI, you can start doing research with it!