GCC Code Coverage Report


Directory: ./
File: Clifford/include/Clifford/CliffTableau.hpp
Date: 2022-10-15 05:10:18
Exec Total Coverage
Lines: 13 13 100.0%
Functions: 2 2 100.0%
Branches: 8 14 57.1%
Decisions: 2 2 100.0%

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