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 "SymplecticTableau.hpp" | |||
18 | ||||
19 | namespace tket { | |||
20 | ||||
21 | // Forward declare Circuit for friend converter | |||
22 | class Circuit; | |||
23 | ||||
24 | class UnitaryTableau { | |||
25 | /** | |||
26 | * An implementation of the stabilizer-destabilizer tableau for unitary | |||
27 | * Cliffords in Aaronson & Gottesman, "Improved Simulation of Stabilizer | |||
28 | * Circuits", https://arxiv.org/pdf/quant-ph/0406196.pdf | |||
29 | * | |||
30 | * If a Pauli is placed at an input before the unitary, there is a Pauli | |||
31 | * string over the outputs which would have an equivalent effect. The rows | |||
32 | * correspond to these Pauli strings for X and Z on each input qubit. In other | |||
33 | * terms, the ith X row is the (phaseful) Pauli string P such that P C X_i = | |||
34 | * C, and similarly for the Z rows. | |||
35 | * | |||
36 | * The Z rows generate the stabilizer group for the state prepared when the | |||
37 | * unitary is applied to the initial |0>^n state, with the X rows extending | |||
38 | * this to generate the full n-fold Pauli group. | |||
39 | * | |||
40 | * Qubits indexed using Qubit objects. | |||
41 | */ | |||
42 | public: | |||
43 | /** | |||
44 | * Construct the tableau for the identity over n qubits (given default qubit | |||
45 | * names). | |||
46 | */ | |||
47 | explicit UnitaryTableau(unsigned n); | |||
48 | ||||
49 | /** | |||
50 | * Construct the tableau for the identity over specific qubits. | |||
51 | */ | |||
52 | explicit UnitaryTableau(const qubit_vector_t& qbs); | |||
53 | ||||
54 | /** | |||
55 | * Construct a tableau from the underlying binary matrices. | |||
56 | * Qubits given default names. | |||
57 | * @param xx The X component of the X rows | |||
58 | * @param xz The Z component of the X rows | |||
59 | * @param xph The phases of the X rows | |||
60 | * @param zx The X component of the Z rows | |||
61 | * @param zz The Z component of the Z rows | |||
62 | * @param zph The phases of the Z rows | |||
63 | */ | |||
64 | explicit UnitaryTableau( | |||
65 | const MatrixXb& xx, const MatrixXb& xz, const VectorXb& xph, | |||
66 | const MatrixXb& zx, const MatrixXb& zz, const VectorXb& zph); | |||
67 | ||||
68 | /** | |||
69 | * Other required constructors | |||
70 | */ | |||
71 |
1/2✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | UnitaryTableau(const UnitaryTableau& other) = default; | |
72 | UnitaryTableau(UnitaryTableau&& other) = default; | |||
73 | UnitaryTableau& operator=(const UnitaryTableau& other) = default; | |||
74 | UnitaryTableau& operator=(UnitaryTableau&& other) = default; | |||
75 | ||||
76 | /** | |||
77 | * Read off an X row as a Pauli string | |||
78 | */ | |||
79 | QubitPauliTensor get_xrow(const Qubit& qb) const; | |||
80 | ||||
81 | /** | |||
82 | * Read off a Z row as a Pauli string | |||
83 | */ | |||
84 | QubitPauliTensor get_zrow(const Qubit& qb) const; | |||
85 | ||||
86 | /** | |||
87 | * Combine rows into a single row according to a QubitPauliTensor | |||
88 | */ | |||
89 | QubitPauliTensor get_row_product(const QubitPauliTensor& qpt) const; | |||
90 | ||||
91 | /** | |||
92 | * Access all IDs for the qubits used in the tableau. | |||
93 | */ | |||
94 | std::set<Qubit> get_qubits() const; | |||
95 | ||||
96 | /** | |||
97 | * Transform the tableau according to consuming a Clifford gate at either end | |||
98 | * of the circuit. | |||
99 | */ | |||
100 | void apply_S_at_front(const Qubit& qb); | |||
101 | void apply_S_at_end(const Qubit& qb); | |||
102 | void apply_V_at_front(const Qubit& qb); | |||
103 | void apply_V_at_end(const Qubit& qb); | |||
104 | void apply_CX_at_front(const Qubit& control, const Qubit& target); | |||
105 | void apply_CX_at_end(const Qubit& control, const Qubit& target); | |||
106 | void apply_gate_at_front(OpType type, const qubit_vector_t& qbs); | |||
107 | void apply_gate_at_end(OpType type, const qubit_vector_t& qbs); | |||
108 | ||||
109 | /** | |||
110 | * Transform the tableau according to consuming a Clifford-phase Pauli gadget | |||
111 | * at either end of the circuit. | |||
112 | * | |||
113 | * @param pauli The string of the Pauli gadget | |||
114 | * @param half_pis The Clifford angle: {0, 1, 2, 3} represents {0, pi/2, pi, | |||
115 | * -pi/2} | |||
116 | */ | |||
117 | void apply_pauli_at_front(const QubitPauliTensor& pauli, unsigned half_pis); | |||
118 | void apply_pauli_at_end(const QubitPauliTensor& pauli, unsigned half_pis); | |||
119 | ||||
120 | /** | |||
121 | * Combine two tableaux in sequence. | |||
122 | * Will throw an exception if the tableaux are not over the same set of | |||
123 | * qubits. | |||
124 | * | |||
125 | * @param first first circuit | |||
126 | * @param second second circuit | |||
127 | * | |||
128 | * @return The tableau corresponding to applying \p first, followed by \p | |||
129 | * second | |||
130 | */ | |||
131 | static UnitaryTableau compose( | |||
132 | const UnitaryTableau& first, const UnitaryTableau& second); | |||
133 | ||||
134 | /** | |||
135 | * Gives the UnitaryTableau corresponding to the inverse (dagger) or transpose | |||
136 | * unitary. This is distinct from simply transposing the binary matrix | |||
137 | * representation. Takes time O(N^3) for N qubits. | |||
138 | */ | |||
139 | UnitaryTableau dagger() const; | |||
140 | UnitaryTableau transpose() const; | |||
141 | ||||
142 | /** | |||
143 | * Gives the UnitaryTableau corresponding to the complex conjugate unitary. | |||
144 | * This calls conjugate() on the underlying SymplecticTableau. | |||
145 | */ | |||
146 | UnitaryTableau conjugate() const; | |||
147 | ||||
148 | friend UnitaryTableau circuit_to_unitary_tableau(const Circuit& circ); | |||
149 | friend Circuit unitary_tableau_to_circuit(const UnitaryTableau& tab); | |||
150 | ||||
151 | friend void to_json(nlohmann::json& j, const UnitaryTableau& tab); | |||
152 | friend void from_json(const nlohmann::json& j, UnitaryTableau& tab); | |||
153 | ||||
154 | friend std::ostream& operator<<(std::ostream& os, const UnitaryTableau& tab); | |||
155 | bool operator==(const UnitaryTableau& other) const; | |||
156 | ||||
157 | private: | |||
158 | /** | |||
159 | * The actual binary tableau. | |||
160 | * Rows 0-(n-1) are the X rows, n-(2n-1) are the Z rows. | |||
161 | */ | |||
162 | SymplecticTableau tab_; | |||
163 | ||||
164 | /** Map from qubit IDs to their row/column index in tableau */ | |||
165 | boost::bimap<Qubit, unsigned> qubits_; | |||
166 | }; | |||
167 | ||||
168 | JSON_DECL(UnitaryTableau) | |||
169 | ||||
170 | std::ostream& operator<<(std::ostream& os, const UnitaryTableau& tab); | |||
171 | ||||
172 | } // namespace tket | |||
173 |