In Previous article, logic is created by creating defects (areas without operators) in the surface operators and vertex operators spread on the plane grid. We have seen that qubits and logical $ X $ and logical $ Z $ operators can be defined, and that CNOT operations can be achieved by moving defects around each other. However, this CNOT operation is actually incomplete to be an element of universal quantum calculation. The reason will be explained later. In this article, I'll study about how a complete CNOT can be achieved. In order to realize universal quantum computation, it would be nice if we could prepare an arbitrary unitary operation of 1 qubit, but we will do that next time. Now that you have a general understanding, use the quantum calculation simulator qlazy to check its operation.
The following documents were used as references.
There is one topic I would like to keep in mind before getting into the main subject. In Previous article, it was found that Braiding can express the logical operation of 2 qubits, but the illustration that moves the defect on the plane around is defective. It is extremely difficult to understand what kind of topological winding is realized by changing the time and what kind of logical operation it corresponds to. Therefore, we will introduce a schematic diagram to make it easier to understand the topological features in 3D space-time.
First, consider the movement of p-type defects. Suppose there is a p-type defect pair as shown on the left in the figure below. By adding a chain of $ X $ operators connecting this defect to the generator, we were able to express the logical $ \ ket {+} $, which is the unique state of the logical $ X $ (upper in the figure below). This state is represented by the symbol $ \ ket {+} ^ {p} $, which means the logic $ \ ket {+} $ created by the p-type defect (subscript L as shown in the figure). I want to write on the right side of the ket, but for some reason it doesn't render well, I'm sorry, please think that there is an L, and so on). This is schematically shown in the upper right of the figure below. Here, the vertical direction represents the spatial position (think of mapping a 2D plane to 1D), and the horizontal direction represents the time axis. Then the time variation of the $ X $ chain will be represented by the curved surface in this space-time. Imagine that the string that connects the defects moves along the time axis and draws a ribbon-like trajectory in space-time. On the other hand, $ \ ket {0} ^ {p} $ can be expressed by adding a loop of the $ Z $ operator that surrounds one of the defects in the defect pair to the generator (lower middle of the figure below). This is schematically shown in the lower right of the figure below. The time variation of this $ Z $ operator loop is also represented by this spatiotemporal surface. However, unlike before, the loop (like a rubber band) that surrounds the defect moves along the time axis, so the trajectory becomes a curved surface like a tube.
Defect pair generation and measurement can also be represented in this schematic diagram. A p-type defect pair could be generated by measuring $ X $ at one point (qubit) in the vacuum state. This can be schematically represented in such a way that a measurement is made somewhere from an empty vacuum and pair production of defect pairs is generated. The state generated in this way is the unique state of the $ X $ chain that connects the two defects. Then, the value as a logical bit can be determined by measuring this $ X $ chain, but the operation will be represented by a diagram in which defects are annihilated [^ 1](see the figure below). ..
[^ 1]: You may get the impression that the defect pair disappears here, but I think that the eigenvalue of the logical qubit is only fixed at +1 or -1, and it does not return to the vacuum state. (Maybe). In order to return to the vacuum state, I think that two defects must be placed next to each other and one of the face operators (loop of the $ Z $ operator) must be measured (please point out if it is wrong). The $ Z $ operator placed around one of the p-type defect pairs can represent the eigenstate of the logical $ Z $ operator, but even if this is measured, the defect does not disappear. No (I think).
Next, consider the d-type defect. The image is exactly the same as the p type except that the positions of the $ X $ operator and the $ Z $ operator are reversed, so it goes smoothly. Suppose there is a d-type defect pair as shown on the left in the figure below. By adding a chain of $ Z $ operators connecting this defect to the generator, $ \ ket {0} ^ {d} $ can be represented (upper in the figure below), and this time change is schematically in the upper right of the figure below. It is represented by a curved surface (ribbon) like. In addition, $ \ ket {+} ^ {d} $ can be represented by a loop of the $ X $ operator that surrounds either of these defect pairs (bottom in the figure below), and this time change is schematically shown on the right in the figure below. It is represented by a curved surface (tube) as shown below.
The generation of d-type defect pairs can be expressed by pair production from the vacuum by $ Z $ measurement to the eigenstate $ \ ket {0} ^ {d} $ of logic $ Z $. Finally, the value as a logical bit can be obtained by measuring the $ Z $ chain, but this operation will be represented by a diagram in which the defect pair disappears (see the figure below).
Now, let's use the schematic diagram introduced in the previous section to represent the CNOT operation realized in Previous article. The figure below shows how a p-type defect pair creates a unique state $ \ ket {+} ^ {p} $ of logic $ X $ and wraps one of the defects around a d-type defect [^] 2]. The chain connecting the p-type defects wraps around the d-type defects, and a new tube (logical $ X $ operator) appears in the d-type defects, and the original p-type ribbon retains its original shape. this is,
[^ 2]: This is a quote from the figure in Reference 3. I have quoted some in other parts of this article. This slide by Professor Fujii is easy to understand with many topological diagrams. it's recommended!
X_1 \otimes I_2 \rightarrow X_1 \otimes X_2 \tag{1}
It corresponds to the logical operation. How do you know? Think of the d-shaped defect below as two rods. Above that is a chain that represents a p-shaped defect, which moves from left to right and the bottom edge of the chain wraps around the bar below. Then, you can imagine that one ring is newly born from one chain [^ 3].
[^ 3]: I think it would be easier to understand if you could explain this situation with a video, but I'm sorry, I don't have the background to make videos. Please do your best and make a video in your head.
The figure below shows the same operation with different initial states. Here, the unique state $ \ ket {0} ^ {d} $ of the logical $ Z $ operator is created with the d-type defect pair, and one of the p-type defects is wrapped around the d-type defect. .. You can see that the chain connecting the d-type defects is wound up by the p-type defects and a new tube (logical $ Z $ operator) appears in the p-type defects. The original d-shaped surface retains its original shape. this is,
I_1 \otimes Z_2 \rightarrow Z_1 \otimes Z_2 \tag{2}
It corresponds to the logical operation. The operation is the same as before, but this time there is a thin film between the two rods representing the d-type defect, and one of the upper p-type defects wraps around the d-type rod and around one of the p-type defects. It is an image that a new ring is born.
In the previous article, he said that CNOT was realized with this. However, if you look closely, the control side is a p-type defect and the target side is a d-type defect. CNOT cannot be performed without this combination. So what's wrong? It seems to be said, but it is not very good. For example
Only CNOT combinations such as are possible. The SWAP gate is a typical unitary operation for two quanta,
As you can see by writing like, it is not feasible if the p-type defect can be used only on the control side of the CNOT operation. In general, unitary operations for any N qubit state can be decomposed into the product of 1 qubit unitary operation and 2 qubit CNOT operations (Reference 4. (See jp / book / 9784274200083 /)), but if there is a restriction that only specific qubits can be control bits (or only specific qubits can be target bits), such decomposition is impossible. In other words, in order to realize universal quantum computation using the surface code of defect pairs, it is necessary to realize CNOT operation in which at least the control bit and the target bit are p-type defects.
Then, how can we realize such a CNOT operation that both the control side and the target side are p-type defects? To be honest, I'm not sure how it was derived, so I'll just say the answer in the references. The answer is to consider the following equivalent circuit of CNOT operation [^ 4].
[^ 4]: This is certainly CNOT, is that okay? I think it's easy to understand if you think about input and output in the stabilizer format. That is, when the input is $ X \ otimes I $, the output is $ X \ otimes X $, or when the input is $ I \ otimes Z $, the output is $ Z \ otimes Z
This is an ordinary quantum circuit that is not a toric code, but the first, third, and fourth qubits from the top are used as control bits, and the second qubit is used as the target bit. It is in shape. So, if you reprint this to the surface code as it is and make the 1st, 3rd and 4th p-type defects and the 2nd d-type defects as shown below, CNOT using only p-type defects. Can be realized.
If you write in the schematic diagram explained earlier, it will look like the figure on the left. The figure on the right is shown below if the winding structure is kept unchanged and re-expressed so that the values are topologically equivalent.
Furthermore, since the spatial dimension was originally two-dimensional, if the topology is accurately expressed in three-dimensional space-time,
It will be. How is it? Somehow a mysterious object-like object of contemporary art appeared, which represents the CNOT operation in the surface code using defects.
Now, let's use the quantum calculation simulator qlazy to see if CNOT operation can really be realized with this. Since Braiding is more complicated than last time, the size of the plane grid to be prepared will be larger by that amount, but there is no problem if you use the stabilizer format because you only need Clifford calculation and measurement.
First, design what kind of Braiding to execute. As long as the defect and its movement are in the topology explained above, it should be possible to decide, so for the time being, I tried the movement as shown in the figure below.
First, create a vacuum state in (1) (spread the surface operator and vertex operator over the entire grid).
Generate 4 defect pairs in (2) (measure the appropriate place and move the defect). Name the qubit numbers 0th, 1st, 2nd, and 3rd from the top of the CNOT circuit diagram shown earlier [^ 5], and arrange them as shown in (2). The 0th, 2nd, and 3rd are p-type defect pairs and are in the eigenstate of logical $ X $, and the first are d-type defect pairs and are in the eigenstate of logical $ Z $. You can prepare a unique state of $ + 1 $ by applying the logical $ Z $ or logical $ X $ operator depending on the measured value.
[^ 5]: Due to the convenience of program implementation, the qubit number will start from 0th. This is almost the case in this blog because it is easier to explain the theory by starting from the first, but in most programming languages, the array index starts from the 0th (FORTRAN certainly has an array index of 1). I think it was from the second). Sometimes I get confused, but I have to get used to it.
Braid the defect in (3). Wrap one of the 0th defect pair around one of the 1st defect pair, and wrap one of the 2nd and 3rd defect pair around the other of the 1st defect pair.
In (4), measure the first defect pair for $ Z $ and the third defect pair for $ X $.
This should implement CNOT. If you want to express the input / output relationship based on $ X $,
Input side control and target | Output side control and target |
---|---|
It becomes [^ 6]. In (2) above, $ \ ket {+} $ was prepared as the control bit (0th qubit) on the input side, and $ \ ket {+} $ was prepared as the target bit (3rd qubit) on the input side. Then, from the first row of this table, the control bit (0th qubit) on the output side is $ \ ket {+} $, and the target bit (2nd qubit) on the output side is $ \ ket {+. } It becomes $. In other words, if you measure the 0th and 2nd qubits for $ X $, you should observe $ (+ 1, + 1) $ with a probability of $ 100 % $. Also, prepare $ \ ket {+} $ as the control bit on the input side, and apply the logical $ Z $ operator to the target bit $ \ ket {+} $ on the input side to set $ \ ket {-} $. If prepared, from the second row of this table, the control and target bits on the output side will be $ \ ket {-} $ and $ \ ket {+} $. In other words, if you measure the 0th and 2nd qubits for $ X $, you should observe $ (-1, + 1) $ with a probability of $ 100 % $. Similarly, you can check the I / O relationship on the 3rd and 4th rows of this table.
[^ 6]: Is this okay? If you think about it in a stabilizer format, you'll know it right away. The first row of the table is $ \ <XI, IX > \ rightarrow \ <XX, IX > = \ <XI, IX > $, the second row is $ \ <XI, -IX > \ rightarrow \ < -XX, IX > = \ <-XI, IX > $, etc.
Here is the entire Python code.
from collections import Counter
from qlazypy import Stabilizer
XBASE = {'0':'+', '1':'-'}
OBJECT = {'p':'face', 'd':'vertex'}
def get_common_qid(obj_A, obj_B):
return list(set(obj_A['dat']) & set(obj_B['dat']))
def get_path(pos_A, pos_B):
path = []
if pos_A[1] < pos_B[1]: h_list = list(range(pos_A[1], pos_B[1] + 1))
else: h_list = list(reversed(range(pos_B[1], pos_A[1] + 1)))
for j in h_list: path.append([pos_A[0], j])
if pos_A[0] < pos_B[0]: v_list = list(range(pos_A[0] + 1, pos_B[0] + 1))
else: v_list = list(reversed(range(pos_B[0], pos_A[0])))
for i in v_list: path.append([i, pos_B[1]])
return path
def create_lattice(row, col):
face = [[None]*col for _ in range(row)]
vertex = [[None]*(col+1) for _ in range(row+1)]
q_row = 2 * row + 1
q_col = 2 * col + 1
q_id = 0
for i in range(q_row):
for j in range(q_col):
if i%2 == 1 and j%2 == 1: # face
dat = []
dat.append((i - 1) * q_col + j) # up
dat.append((i + 1) * q_col + j) # down
dat.append(i * q_col + (j - 1)) # left
dat.append(i * q_col + (j + 1)) # right
face[i//2][j//2] = {'anc': q_id, 'dat': dat}
elif i%2 == 0 and j%2 == 0: # vertex
dat = []
if i > 0: dat.append((i - 1) * q_col + j) # up
if i < q_row - 1: dat.append((i + 1) * q_col + j) # down
if j > 0: dat.append(i * q_col + (j - 1)) # left
if j < q_col - 1: dat.append(i * q_col + (j + 1)) # right
vertex[i//2][j//2] = {'anc': q_id, 'dat': dat}
q_id += 1
return {'face': face, 'vertex': vertex}
def initialize(sb, lattice):
i = 0 # generator id
for face_list in lattice['face']:
for face in face_list:
[sb.set_pauli_op(i, q, 'Z') for q in face['dat']]
i += 1
sb.set_pauli_op(i, face['anc'], 'Z')
i += 1
for vertex_list in lattice['vertex']:
for vertex in vertex_list:
[sb.set_pauli_op(i, q, 'X') for q in vertex['dat']]
i += 1
sb.set_pauli_op(i, vertex['anc'], 'Z')
i += 1
def get_chain(pos_list, dtype, lattice):
chain = []
for i in range(1,len(pos_list)):
pos_A = pos_list[i-1]
pos_B = pos_list[i]
chain.append(get_common_qid(lattice[OBJECT[dtype]][pos_A[0]][pos_A[1]],
lattice[OBJECT[dtype]][pos_B[0]][pos_B[1]])[0])
return chain
def move_defect(sb, pos_A, pos_B, path, dtype, lattice, create=False, annihilate=False):
obj = OBJECT[dtype]
if create == True:
obj_A = lattice[obj][pos_A[0]][pos_A[1]]
obj_B = lattice[obj][pos_B[0]][pos_B[1]]
q = get_common_qid(obj_A, obj_B)[0]
if dtype == 'p':
md = sb.mx(qid=[q])
if md.last == '1': [sb.z(i) for i in obj_B['dat']]
elif dtype == 'd':
md = sb.m(qid=[q])
if md.last == '1': [sb.x(i) for i in obj_B['dat']]
chain = get_chain(get_path(pos_A, pos_B), dtype, lattice)
for i in range(1,len(path)):
# extend defect
obj_A = lattice[obj][path[i-1][0]][path[i-1][1]]
obj_B = lattice[obj][path[i][0]][path[i][1]]
q = get_common_qid(obj_A, obj_B)[0]
if dtype == 'p':
md = sb.mx(qid=[q])
if md.last == '1': [sb.z(i) for i in obj_B['dat']]
elif dtype == 'd':
md = sb.m(qid=[q])
if md.last == '1': [sb.x(i) for i in obj_B['dat']]
# remove defect
sb.h(obj_A['anc'])
if dtype == 'p': [sb.cz(obj_A['anc'], target) for target in obj_A['dat']]
elif dtype == 'd': [sb.cx(obj_A['anc'], target) for target in obj_A['dat']]
sb.h(obj_A['anc'])
md = sb.m(qid=[obj_A['anc']])
if md.last == '1':
if dtype == 'p': [sb.x(i) for i in chain]
elif dtype == 'd': [sb.z(i) for i in chain]
sb.x(obj_A['anc'])
chain.append(q)
if annihilate == True:
obj_A = lattice[obj][pos_A[0]][pos_A[1]]
obj_B = lattice[obj][path[-1][0]][path[-1][1]]
q = get_common_qid(obj_A, obj_B)[0]
if dtype == 'p': md = sb.mx(qid=[q])
elif dtype == 'd': md = sb.m(qid=[q])
return md.last
return None
def measure_logical_X(sb, chain_A, chain_B, shots=1):
mval_list = []
for _ in range(shots):
sb_tmp = sb.clone()
mval_A = sb_tmp.mx(qid=chain_A).last
mval_B = sb_tmp.mx(qid=chain_B).last
mval_A_bin = str(sum([int(s) for s in list(mval_A)])%2)
mval_B_bin = str(sum([int(s) for s in list(mval_B)])%2)
mval = (XBASE[mval_A_bin] + XBASE[mval_B_bin])
mval_list.append(mval)
sb_tmp.free()
return Counter(mval_list)
def operate_logical_Z(sb, lq, lattice):
if lq == 0: face = lattice['face'][0][0]
elif lq == 2: face = lattice['face'][0][7]
elif lq == 3: face = lattice['face'][6][0]
[sb.z(q) for q in face['dat']]
def operate_logical_cnot(lq, shots=5):
# set lattice
lattice_row, lattice_col = 7, 8
lattice = create_lattice(lattice_row, lattice_col)
# make vacuum state
qubit_num = (2 * lattice_row + 1) * (2 * lattice_col + 1)
sb = Stabilizer(qubit_num=qubit_num, gene_num=qubit_num+1)
initialize(sb, lattice)
# set logical qubit #0
p0_pos_A, p0_pos_B = [0,0], [0,1]
p0_path = [[0,1],[0,2]]
move_defect(sb, p0_pos_A, p0_pos_B, p0_path, 'p', lattice, create=True)
if lq[0] == '-': operate_logical_Z(sb, 0, lattice)
# set logical qubit #1
d1_pos_A, d1_pos_B = [2,5], [3,5]
d1_path = [[3,5],[4,5],[5,5]]
move_defect(sb, d1_pos_A, d1_pos_B, d1_path, 'd', lattice, create=True)
# set logical qubit #2
p2_pos_A, p2_pos_B = [0,7], [1,7]
p2_path = [[1,7],[2,7],[3,7]]
move_defect(sb, p2_pos_A, p2_pos_B, p2_path, 'p', lattice, create=True)
# set logical qubit #3
p3_pos_A, p3_pos_B = [6,0], [6,1]
p3_path = [[6,1],[6,2]]
move_defect(sb, p3_pos_A, p3_pos_B, p3_path, 'p', lattice, create=True)
if lq[1] == '-': operate_logical_Z(sb, 3, lattice)
# braid logical qubit #0
p0_pos_A, p0_pos_B = [0,0], [0,2]
p0_path = [[0,2],[1,2],[2,2],[3,2],[3,3],[3,4],[3,5],[3,6],
[2,6],[1,6],[0,6],[0,5],[0,4],[0,3],[0,2]]
move_defect(sb, p0_pos_A, p0_pos_B, p0_path, 'p', lattice)
# braid logical qubit #2
p2_pos_A, p2_pos_B = [0,7], [3,7]
p2_path = [[3,7],[3,6],[3,5],[3,4],[3,3],[4,3],[5,3],
[6,3],[6,4],[6,5],[6,6],[6,7],[5,7],[4,7],[3,7]]
move_defect(sb, p2_pos_A, p2_pos_B, p2_path, 'p', lattice)
# braid and annihilate logical qubit #3
p3_pos_A, p3_pos_B = [6,0], [6,2]
p3_path = [[6,2],[6,3],[6,4],[6,5],[6,6],[5,6],[4,6],[3,6],
[3,5],[3,4],[3,3],[3,2],[4,2],[5,2],[6,2],[6,1]]
mval_p = move_defect(sb, p3_pos_A, p3_pos_B, p3_path, 'p', lattice, annihilate=True)
# braid and annihilate logical qubit #1
d1_pos_A, d1_pos_B = [2,5], [5,5]
d1_path = [[5,5],[4,5],[3,5]]
mval_d = move_defect(sb, d1_pos_A, d1_pos_B, d1_path, 'd', lattice, annihilate=True)
if mval_p == '1':
operate_logical_Z(sb, 0, lattice)
operate_logical_Z(sb, 2, lattice)
# measure logical qubits: #0 and #2
chain_0 = get_chain(get_path([0,0],[0,2]), 'p', lattice)
chain_2 = get_chain(get_path([0,7],[3,7]), 'p', lattice)
freq = measure_logical_X(sb, chain_0, chain_2, shots=shots)
print("Input('{0:}') == [CNOT] ==> {1:}".format(lq, freq))
sb.free()
if __name__ == '__main__':
operate_logical_cnot('++', shots=10) # --> '++'
operate_logical_cnot('+-', shots=10) # --> '--'
operate_logical_cnot('-+', shots=10) # --> '-+'
operate_logical_cnot('--', shots=10) # --> '+-'
It's been quite long, so I'll explain it roughly. The operate_logical_cnot function is the main part of this time. By giving the input state as a character string ('++','+-','-+','-') as the first argument and giving the number of measurements as an integer value in the second argument, the measurement result can be obtained. Display.
Look inside the operation_logical_cnot function.
# set lattice
lattice_row, lattice_col = 7, 8
lattice = create_lattice(lattice_row, lattice_col)
Then, set the vertical and horizontal sizes of the grid to $ 7 \ times 8 $ as explained above, and create the grid data with the create_lattice function. This is the same as Previous article, so I will omit the explanation.
# make vacuum state
qubit_num = (2 * lattice_row + 1) * (2 * lattice_col + 1)
sb = Stabilizer(qubit_num=qubit_num, gene_num=qubit_num+1)
initialize(sb, lattice)
Then, give the number of qubits and the number of generators corresponding to the created grid to the stabilizer constructor Stabilizer to create the instance sb. Then use the initialize function to evacuate it. That is, spread the face and vertex operators across the grid. See the function definition above for details. I just set the $ X $ or $ Z $ operator to each qubit using sb's set_pauli_op method.
# set logical qubit #0
p0_pos_A, p0_pos_B = [0,0], [0,1]
p0_path = [[0,1],[0,2]]
move_defect(sb, p0_pos_A, p0_pos_B, p0_path, 'p', lattice, create=True)
if lq[0] == '-': operate_logical_Z(sb, 0, lattice)
Generates the 0th logical qubit. With the move_defect function, the qubit at the boundary of the coordinates [0,0], [0,1] of the surface operator is measured by $ X $ to generate a pair of p-type defects, and the defect of [0,1] is generated. The process of moving to the coordinates [0,2] is being performed. If you set the create option of the move_defect function to True, it will be created and then moved. If False (default), only move (assuming it has already been generated) without generating it. Also, as we will see later, if you specify True for this because of the annihilate option, the measurement will be performed after moving (assuming that the defect pairs are adjacent). See the function definition for more information on the make_defect function. This implements a logical $ \ ket {+} $. The last if lq [0] =='-' is an operation to invert by applying the logical $ Z $ operator when the 0th input state given to the operate_logical_cnot function is'-'. See also the function definition for more information on the operate_logical_Z function.
# set logical qubit #1
d1_pos_A, d1_pos_B = [2,5], [3,5]
d1_path = [[3,5],[4,5],[5,5]]
move_defect(sb, d1_pos_A, d1_pos_B, d1_path, 'd', lattice, create=True)
# set logical qubit #2
p2_pos_A, p2_pos_B = [0,7], [1,7]
p2_path = [[1,7],[2,7],[3,7]]
move_defect(sb, p2_pos_A, p2_pos_B, p2_path, 'p', lattice, create=True)
# set logical qubit #3
p3_pos_A, p3_pos_B = [6,0], [6,1]
p3_path = [[6,1],[6,2]]
move_defect(sb, p3_pos_A, p3_pos_B, p3_path, 'p', lattice, create=True)
if lq[1] == '-': operate_logical_Z(sb, 3, lattice)
so. Generates the first, second, and third logical qubits. This completes the initial state.
# braid logical qubit #0
p0_pos_A, p0_pos_B = [0,0], [0,2]
p0_path = [[0,2],[1,2],[2,2],[3,2],[3,3],[3,4],[3,5],[3,6],
[2,6],[1,6],[0,6],[0,5],[0,4],[0,3],[0,2]]
move_defect(sb, p0_pos_A, p0_pos_B, p0_path, 'p', lattice)
Then, the 0th logical qubit (p-type defect) is wound around the 1st logical qubit (d-type defect).
# braid logical qubit #2
p2_pos_A, p2_pos_B = [0,7], [3,7]
p2_path = [[3,7],[3,6],[3,5],[3,4],[3,3],[4,3],[5,3],
[6,3],[6,4],[6,5],[6,6],[6,7],[5,7],[4,7],[3,7]]
move_defect(sb, p2_pos_A, p2_pos_B, p2_path, 'p', lattice)
Then, the second logical qubit (p-type defect) is wound around the first logical qubit (d-type defect).
# braid and annihilate logical qubit #3
p3_pos_A, p3_pos_B = [6,0], [6,2]
p3_path = [[6,2],[6,3],[6,4],[6,5],[6,6],[5,6],[4,6],[3,6],
[3,5],[3,4],[3,3],[3,2],[4,2],[5,2],[6,2],[6,1]]
mval_p = move_defect(sb, p3_pos_A, p3_pos_B, p3_path, 'p', lattice, annihilate=True)
Then, the third logical qubit (p-type defect) is wound around the first logical qubit (d-type defect), and finally it disappears. Since the move_defect function is designed to return the measured value at the time of extinction, keep it as mval_p (will be used later).
# braid and annihilate logical qubit #1
d1_pos_A, d1_pos_B = [2,5], [5,5]
d1_path = [[5,5],[4,5],[3,5]]
mval_d = move_defect(sb, d1_pos_A, d1_pos_B, d1_path, 'd', lattice, annihilate=True)
Measure the first logical qubit (d-type defect). Let the measured value be mval_d.
if mval_p == '1':
operate_logical_Z(sb, 0, lattice)
operate_logical_Z(sb, 2, lattice)
So, if the value of mval_p (measured value at the time of extinction of the 3rd logical qubit) is -1 (1 as a measurement index), the last 0th and 2nd logical qubits are inverted. I will.
# measure logical qubits: #0 and #2
chain_0 = get_chain(get_path([0,0],[0,2]), 'p', lattice)
chain_2 = get_chain(get_path([0,7],[3,7]), 'p', lattice)
freq = measure_logical_X(sb, chain_0, chain_2, shots=shots)
print("Input('{0:}') == [CNOT] ==> {1:}".format(lq, freq))
Then, to check if the CNOT operation is performed, measure the 0th logical qubit and the 2nd logical qubit for $ X $. Since both are p-type defects, you can measure the chain of $ X $ operators that connect the defects. I'm doing that with the measure_logical_X operator. The measurement result is stored in freq in Counter format, so it is displayed at the end. that's all.
The execution result is as follows.
Input('++') == [CNOT] ==> Counter({'++': 10})
Input('+-') == [CNOT] ==> Counter({'--': 10})
Input('-+') == [CNOT] ==> Counter({'-+': 10})
Input('--') == [CNOT] ==> Counter({'+-': 10})
For all four input patterns, there is a $ 100 % $ probability of getting the correct CNOT operation results.
It is very interesting to be able to perform logical operations with Braiding like this, but it is quite difficult to implement it on actual hardware. There are a lot of defects on the grid plane and we have to perform the operation to wind them around, but the initial arrangement and winding method are not the same, and in the first place it is not good in front of a lot of defects. When implemented, the circuit = program may be in the spaghetti state (or rather, logical operations could be performed by putting it in the spaghetti state, hmm). Perhaps this is the role of the compiler, so the general public may not care about it, but it is a big problem for those who are going to make a compiler from now on. After all, is Lattice Surgery cleaner and better outlook than Braiding (although I haven't studied yet).
When I was thinking about that while writing the draft of this article, I made the following announcement.
-[Development of circuit compression method to realize miniaturization and high speed of quantum computer-Accelerate large-scale quantum computer development with new software technology-](https://www.nii.ac.jp/news/release /2020/1112.html)
It seems that circuit compression can be done efficiently by using something called ZX calculus based on Braiding instead of Lattice Surgery (how). You may need to check it out! (But what to do before that ...)
that's all