Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | // Copyright 2019-2022 Cambridge Quantum Computing | |||
2 | // | |||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |||
4 | // you may not use this file except in compliance with the License. | |||
5 | // You may obtain a copy of the License at | |||
6 | // | |||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | |||
8 | // | |||
9 | // Unless required by applicable law or agreed to in writing, software | |||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
12 | // See the License for the specific language governing permissions and | |||
13 | // limitations under the License. | |||
14 | ||||
15 | #pragma once | |||
16 | ||||
17 | #include "OpType/OpType.hpp" | |||
18 | #include "Utils/BiMapHeaders.hpp" | |||
19 | #include "Utils/MatrixAnalysis.hpp" | |||
20 | #include "Utils/PauliStrings.hpp" | |||
21 | ||||
22 | namespace tket { | |||
23 | ||||
24 | class Circuit; | |||
25 | ||||
26 | class CliffTableau { | |||
27 | /* | |||
28 | * Based off of the stabiliser-destabiliser tableau in Aaronson & Gottesman, | |||
29 | * "Improved Simulation of Stabilizer Circuits", | |||
30 | * https://arxiv.org/pdf/quant-ph/0406196.pdf | |||
31 | * | |||
32 | * This paper uses the rows of the tableau to describe the generators for the | |||
33 | * set of stabilizers/destabilizers of a state. They then start with the state | |||
34 | * |0>^n and perform column operations for each gate to show how all | |||
35 | * generators are affected by the interacting qubits (columns). | |||
36 | * | |||
37 | * We flip this and instead have rows representing how the output of each wire | |||
38 | * is affected by the inputs. The ZPauli on a given wire is the Pauli operator | |||
39 | * that would be applied if an Rz gate were applied there and commuted through | |||
40 | * to the inputs, similarly for the XPauli and Rx. This is the same as the | |||
41 | * operator one would measure on the inputs if a standard/Hadamard basis | |||
42 | * measurement were applied on the wire. Hence applying gates performs row | |||
43 | * operations on the interacting wires. | |||
44 | * | |||
45 | * We can also consider the effect of adding a gate at the start of our | |||
46 | * circuit (bringing it from the environment into the box) by column | |||
47 | * operations. | |||
48 | */ | |||
49 | public: | |||
50 | /** | |||
51 | * Construct the tableau for the identity over n qubits (given default qubit | |||
52 | * names). | |||
53 | */ | |||
54 | explicit CliffTableau(unsigned n); | |||
55 | ||||
56 | /** | |||
57 | * Construct the tableau for the identity over specific qubits. | |||
58 | */ | |||
59 | explicit CliffTableau(const qubit_vector_t &qbs); | |||
60 | ||||
61 | /** | |||
62 | * Copy constructor | |||
63 | */ | |||
64 | 39 | CliffTableau(const CliffTableau &other) | ||
65 | 39 | : size_(other.size_), | ||
66 | 39 | xpauli_x(other.xpauli_x), | ||
67 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | xpauli_z(other.xpauli_z), | |
68 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | xpauli_phase(other.xpauli_phase), | |
69 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | zpauli_x(other.zpauli_x), | |
70 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | zpauli_z(other.zpauli_z), | |
71 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | zpauli_phase(other.zpauli_phase), | |
72 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | qubits_(other.qubits_){}; | |
73 | ||||
74 | /** | |||
75 | * Access the Pauli string on the Z-channel of a given qubit with respect to | |||
76 | * all inputs. | |||
77 | */ | |||
78 | QubitPauliTensor get_zpauli(const Qubit &qb) const; | |||
79 | ||||
80 | /** | |||
81 | * Access the Pauli string on the X-channel of a given qubit with respect to | |||
82 | * all inputs. | |||
83 | */ | |||
84 | QubitPauliTensor get_xpauli(const Qubit &qb) const; | |||
85 | ||||
86 | /** | |||
87 | * Access all IDs for the qubits captured by the tableau. | |||
88 | */ | |||
89 | std::set<Qubit> get_qubits() const; | |||
90 | ||||
91 | /** | |||
92 | * Transform the tableau according to consuming a Clifford gate at either | |||
93 | * end of the circuit. | |||
94 | * Args are indices in the tableau's qubit ordering, so may not necessarily | |||
95 | * match up to register indices. | |||
96 | */ | |||
97 | void apply_S_at_front(unsigned qb); | |||
98 | void apply_S_at_end(unsigned qb); | |||
99 | void apply_V_at_front(unsigned qb); | |||
100 | void apply_V_at_end(unsigned qb); | |||
101 | void apply_CX_at_front(unsigned control, unsigned target); | |||
102 | void apply_CX_at_end(unsigned control, unsigned target); | |||
103 | ||||
104 | /** | |||
105 | * Transform the tableau according to consuming a Clifford gate at either | |||
106 | * end of the circuit. | |||
107 | */ | |||
108 | void apply_gate_at_front(OpType type, const std::vector<unsigned> &qbs); | |||
109 | void apply_gate_at_front(OpType type, const qubit_vector_t &qbs); | |||
110 | void apply_gate_at_end(OpType type, const std::vector<unsigned> &qbs); | |||
111 | void apply_gate_at_end(OpType type, const qubit_vector_t &qbs); | |||
112 | ||||
113 | /** | |||
114 | * Transform the tableau according to consuming a Clifford-phase pauli | |||
115 | * gadget at either end of the circuit. | |||
116 | * | |||
117 | * @param pauli The string of the pauli gadget | |||
118 | * @param half_pis The Clifford angle: {0, 1, 2, 3} represents {0, pi/2, pi, | |||
119 | * -pi/2} | |||
120 | */ | |||
121 | void apply_pauli_at_front(const QubitPauliTensor &pauli, unsigned half_pis); | |||
122 | void apply_pauli_at_end(const QubitPauliTensor &pauli, unsigned half_pis); | |||
123 | ||||
124 | /** | |||
125 | * Combine two tableaus in sequence. | |||
126 | * Will throw an exception if the tableaus are not over the same set of | |||
127 | * qubits. | |||
128 | * | |||
129 | * @param first first circuit | |||
130 | * @param second second circuit | |||
131 | * | |||
132 | * @return The tableau corresponding to applying \p first, followed by \p | |||
133 | * second | |||
134 | */ | |||
135 | static CliffTableau compose( | |||
136 | const CliffTableau &first, const CliffTableau &second); | |||
137 | ||||
138 | friend CliffTableau circuit_to_tableau(const Circuit &circ); | |||
139 | friend Circuit tableau_to_circuit(const CliffTableau &tab); | |||
140 | ||||
141 | friend std::ostream &operator<<(std::ostream &os, CliffTableau const &tab); | |||
142 | bool operator==(const CliffTableau &other) const; | |||
143 | ||||
144 | private: | |||
145 | /** Number of qubits */ | |||
146 | const unsigned size_; | |||
147 | ||||
148 | /** | |||
149 | * (A)pauli_(B): matrix representing the dependence of the A-channel of each | |||
150 | * output on the (B) channel of each input | |||
151 | * | |||
152 | * (A)pauli_phase: whether or not there is a phase-flip on the A-channel of | |||
153 | * each output | |||
154 | */ | |||
155 | MatrixXb xpauli_x; | |||
156 | MatrixXb xpauli_z; | |||
157 | VectorXb xpauli_phase; | |||
158 | MatrixXb zpauli_x; | |||
159 | MatrixXb zpauli_z; | |||
160 | VectorXb zpauli_phase; | |||
161 | ||||
162 | /** Map from qubit IDs to their row/column index in tableau */ | |||
163 | boost::bimap<Qubit, unsigned> qubits_; | |||
164 | ||||
165 | /** | |||
166 | * Boolean encoding of Pauli | |||
167 | * <x, z> = <false, false> ==> I | |||
168 | * <x, z> = <false, true> ==> Z | |||
169 | * <x, z> = <true, false> ==> X | |||
170 | * <x, z> = <true, true> ==> Y | |||
171 | */ | |||
172 | struct BoolPauli { | |||
173 | bool x; | |||
174 | bool z; | |||
175 | ||||
176 | /** | |||
177 | * Lexicographic ordering by <x, z> | |||
178 | */ | |||
179 | 101054 | bool operator<(const BoolPauli &other) const { | ||
180 |
2/2✓ Branch 0 taken 87204 times.
✓ Branch 1 taken 13850 times.
|
2/2✓ Decision 'true' taken 87204 times.
✓ Decision 'false' taken 13850 times.
|
101054 | if (x == other.x) { |
181 | 87204 | return z < other.z; | ||
182 | } | |||
183 | 13850 | return x < other.x; | ||
184 | } | |||
185 | }; | |||
186 | ||||
187 | /** | |||
188 | * Look-up table for Pauli multiplication with boolean encoding | |||
189 | */ | |||
190 | static const std::map< | |||
191 | std::pair<BoolPauli, BoolPauli>, std::pair<BoolPauli, Complex>> | |||
192 | mult_lut; | |||
193 | ||||
194 | /** | |||
195 | * Helper methods for manipulating the tableau when applying gates | |||
196 | */ | |||
197 | void row_mult( | |||
198 | const MatrixXb::RowXpr &xa, const MatrixXb::RowXpr &za, const bool &pa, | |||
199 | const MatrixXb::RowXpr &xb, const MatrixXb::RowXpr &zb, const bool &pb, | |||
200 | Complex phase, MatrixXb::RowXpr &xw, MatrixXb::RowXpr &zw, bool &pw); | |||
201 | void col_mult( | |||
202 | const MatrixXb::ColXpr &a, const MatrixXb::ColXpr &b, bool flip, | |||
203 | MatrixXb::ColXpr &w, VectorXb &pw); | |||
204 | }; | |||
205 | ||||
206 | std::ostream &operator<<(std::ostream &os, CliffTableau const &tab); | |||
207 | ||||
208 | } // namespace tket | |||
209 |