|
1 | 1 | from qutip_qip.circuit import QubitCircuit
|
2 | 2 |
|
3 |
| - |
4 | 3 | class BitFlipCode:
|
5 | 4 | """
|
6 |
| - Generalized implementation of the 3-qubit bit-flip code. |
| 5 | + Implementation of the 3-qubit bit-flip code using projective measurements |
| 6 | + and classically controlled gates for automatic correction. |
| 7 | +
|
| 8 | + The class represents the abstract structure of the bit-flip code. |
| 9 | + Qubit indices must be provided when generating the circuit. |
7 | 10 |
|
8 |
| - Parameters |
9 |
| - ---------- |
10 |
| - data_qubits : list of int |
11 |
| - The three physical qubits holding the logical qubit. |
12 |
| - syndrome_qubits : list of int, optional |
13 |
| - Two ancilla qubits used for syndrome extraction. |
| 11 | + Methods |
| 12 | + ------- |
| 13 | + encode_circuit(data_qubits) |
| 14 | + Encode a logical qubit into three physical qubits. |
| 15 | + syndrome_and_correction_circuit(data_qubits, syndrome_qubits) |
| 16 | + Extract error syndrome and apply correction via classical control. |
| 17 | + decode_circuit(data_qubits) |
| 18 | + Decode the logical qubit back to a single physical qubit. |
14 | 19 | """
|
15 | 20 |
|
16 |
| - def __init__(self, data_qubits=[0, 1, 2], syndrome_qubits=[3, 4]): |
17 |
| - assert len(data_qubits) == 3, "Bit-flip code requires 3 data qubits." |
18 |
| - self.data_qubits = data_qubits |
19 |
| - self.syndrome_qubits = syndrome_qubits |
20 |
| - self.n_qubits = max(data_qubits + syndrome_qubits) + 1 |
| 21 | + def __init__(self): |
| 22 | + self.n_data = 3 |
| 23 | + self.n_syndrome = 2 |
21 | 24 |
|
22 |
| - def encode_circuit(self): |
23 |
| - """ |
24 |
| - Returns |
25 |
| - ------- |
26 |
| - QubitCircuit |
27 |
| - Circuit encoding the logical qubit into 3 physical qubits. |
28 |
| - """ |
29 |
| - qc = QubitCircuit(self.n_qubits) |
30 |
| - control = self.data_qubits[0] |
31 |
| - for target in self.data_qubits[1:]: |
| 25 | + def encode_circuit(self, data_qubits): |
| 26 | + assert len(data_qubits) == self.n_data |
| 27 | + qc = QubitCircuit(max(data_qubits) + 1) |
| 28 | + control = data_qubits[0] |
| 29 | + for target in data_qubits[1:]: |
32 | 30 | qc.add_gate("CNOT", controls=control, targets=target)
|
33 | 31 | return qc
|
34 | 32 |
|
35 |
| - def syndrome_measurement_circuit(self): |
36 |
| - """ |
37 |
| - Returns |
38 |
| - ------- |
39 |
| - QubitCircuit |
40 |
| - Circuit to extract the error syndrome using ancilla qubits. |
41 |
| - """ |
42 |
| - qc = QubitCircuit(self.n_qubits) |
43 |
| - dq = self.data_qubits |
44 |
| - sq = self.syndrome_qubits |
| 33 | + def syndrome_and_correction_circuit(self, data_qubits, syndrome_qubits): |
| 34 | + assert len(data_qubits) == self.n_data |
| 35 | + assert len(syndrome_qubits) == self.n_syndrome |
| 36 | + |
| 37 | + total_qubits = max(data_qubits + syndrome_qubits) + 1 |
| 38 | + classical_bits = len(syndrome_qubits) |
| 39 | + qc = QubitCircuit(N=total_qubits, num_cbits=classical_bits) |
| 40 | + |
| 41 | + dq = data_qubits |
| 42 | + sq = syndrome_qubits |
45 | 43 |
|
46 |
| - # First syndrome bit: parity of dq[0] and dq[1] |
| 44 | + # Syndrome extraction |
47 | 45 | qc.add_gate("CNOT", controls=dq[0], targets=sq[0])
|
48 | 46 | qc.add_gate("CNOT", controls=dq[1], targets=sq[0])
|
49 |
| - |
50 |
| - # Second syndrome bit: parity of dq[1] and dq[2] |
51 | 47 | qc.add_gate("CNOT", controls=dq[1], targets=sq[1])
|
52 | 48 | qc.add_gate("CNOT", controls=dq[2], targets=sq[1])
|
53 | 49 |
|
54 |
| - return qc |
55 |
| - |
56 |
| - def correction_circuit(self, syndrome): |
57 |
| - """ |
58 |
| - Parameters |
59 |
| - ---------- |
60 |
| - syndrome : tuple |
61 |
| - Two-bit syndrome measurement result (s1, s2). |
| 50 | + # Measurements into classical bits |
| 51 | + qc.add_measurement(sq[0], classical_store=0) |
| 52 | + qc.add_measurement(sq[1], classical_store=1) |
62 | 53 |
|
63 |
| - Returns |
64 |
| - ------- |
65 |
| - QubitCircuit |
66 |
| - Circuit applying the appropriate X gate based on syndrome. |
67 |
| - """ |
68 |
| - qc = QubitCircuit(self.n_qubits) |
69 |
| - s1, s2 = syndrome |
| 54 | + # Classically controlled correction |
| 55 | + qc.add_gate("X", targets=dq[0], classical_controls=[0, 1], classical_control_value=[1, 0]) |
| 56 | + qc.add_gate("X", targets=dq[1], classical_controls=[0, 1], classical_control_value=[1, 1]) |
| 57 | + qc.add_gate("X", targets=dq[2], classical_controls=[0, 1], classical_control_value=[0, 1]) |
70 | 58 |
|
71 |
| - if s1 == 1 and s2 == 0: |
72 |
| - qc.add_gate("X", targets=self.data_qubits[0]) |
73 |
| - elif s1 == 1 and s2 == 1: |
74 |
| - qc.add_gate("X", targets=self.data_qubits[1]) |
75 |
| - elif s1 == 0 and s2 == 1: |
76 |
| - qc.add_gate("X", targets=self.data_qubits[2]) |
77 |
| - # No correction for (0,0) |
78 | 59 | return qc
|
79 | 60 |
|
80 |
| - def decode_circuit(self): |
81 |
| - """ |
82 |
| - Returns |
83 |
| - ------- |
84 |
| - QubitCircuit |
85 |
| - Circuit to decode the logical qubit back to original qubit. |
86 |
| - """ |
87 |
| - qc = QubitCircuit(self.n_qubits) |
88 |
| - control = self.data_qubits[0] |
89 |
| - for target in reversed(self.data_qubits[1:]): |
| 61 | + def decode_circuit(self, data_qubits): |
| 62 | + assert len(data_qubits) == self.n_data |
| 63 | + qc = QubitCircuit(max(data_qubits) + 1) |
| 64 | + control = data_qubits[0] |
| 65 | + for target in reversed(data_qubits[1:]): |
90 | 66 | qc.add_gate("CNOT", controls=control, targets=target)
|
91 | 67 |
|
92 |
| - # Optional TOFFOLI to verify parity |
93 |
| - qc.add_gate("TOFFOLI", controls=self.data_qubits[1:], targets=control) |
| 68 | + # Optional parity verification |
| 69 | + qc.add_gate("TOFFOLI", controls=data_qubits[1:], targets=control) |
94 | 70 | return qc
|
0 commit comments