tket
Loading...
Searching...
No Matches
GreedyPauliOps.cpp
Go to the documentation of this file.
1// Copyright Quantinuum
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#include <algorithm>
16
22
23namespace tket {
24
25namespace Transforms {
26
27namespace GreedyPauliSimp {
28
29static CommuteType get_pauli_pair_commute_type(
30 const Pauli& p0, const Pauli& p1) {
31 if (p0 == Pauli::I && p1 == Pauli::I) {
32 return CommuteType::I;
33 }
34 if (p0 == p1 || p0 == Pauli::I || p1 == Pauli::I) {
35 return CommuteType::C;
36 }
37 return CommuteType::A;
38}
39
40// PauliNode abstract class
41
43
44void PauliNode::update(const OpType& /*sq_cliff*/, const unsigned& /*a*/) {
45 throw GreedyPauliSimpError("Single qubit Clifford update not implemented.");
46}
47
48void PauliNode::swap(const unsigned& /*a*/, const unsigned& /*b*/) {
49 throw GreedyPauliSimpError("SWAP update not implemented.");
50}
51
52// SingleNode
53
54SingleNode::SingleNode(std::vector<Pauli> string, bool sign)
55 : string_(string), sign_(sign) {
56 if (string.empty()) {
57 throw GreedyPauliSimpError("SingleNode cannot have empty strings.");
58 }
59 weight_ =
60 string_.size() - std::count(string_.begin(), string_.end(), Pauli::I);
61 if (weight_ == 0) {
63 "SingleNode cannot be constructed with identity strings.");
64 }
65}
66
67unsigned SingleNode::tqe_cost() const { return weight_ - 1; }
68
69int SingleNode::tqe_cost_increase(const TQE& tqe) const {
70 const TQEType& g = tqe.type;
71 const unsigned& a = tqe.a;
72 const unsigned& b = tqe.b;
73 Pauli p0 = string_[a];
74 Pauli p1 = string_[b];
75 return TQE_PAULI_MAP::cost_increase({g, p0, p1});
76}
77
78void SingleNode::update(const TQE& tqe) {
79 const TQEType& g = tqe.type;
80 const unsigned& a = tqe.a;
81 const unsigned& b = tqe.b;
82 const Pauli p0 = string_[a];
83 const Pauli p1 = string_[b];
84 const TQE_PAULI_MAP::Key key{g, p0, p1};
86 return;
87 }
88 const auto [new_p0, new_p1, sign] = TQE_PAULI_MAP::at(key);
89 string_[a] = new_p0;
90 string_[b] = new_p1;
92 if (!sign) {
93 sign_ = !sign_;
94 }
95}
96
97std::vector<TQE> SingleNode::reduction_tqes() const {
98 // qubits with support
99 std::vector<unsigned> sqs;
100 for (unsigned i = 0; i < string_.size(); i++) {
101 if (string_[i] != Pauli::I) sqs.push_back(i);
102 }
103 TKET_ASSERT(!sqs.empty());
104 std::vector<TQE> tqes;
105 tqes.reserve(sqs.size() * (sqs.size() - 1) * 2);
106 for (unsigned i = 0; i < sqs.size() - 1; i++) {
107 for (unsigned j = i + 1; j < sqs.size(); j++) {
108 const auto& tqe_types =
109 TQE_REDUCTION_MAP.at({string_[sqs[i]], string_[sqs[j]]});
110 for (const TQEType& tt : tqe_types) {
111 tqes.push_back({tt, sqs[i], sqs[j]});
112 }
113 }
114 }
115 return tqes;
116}
117
118std::pair<unsigned, Pauli> SingleNode::first_support() const {
119 for (unsigned i = 0; i < string_.size(); i++) {
120 if (string_[i] != Pauli::I) {
121 return {i, string_[i]};
122 }
123 }
124 // Should be impossible to reach here
125 TKET_ASSERT(false);
126}
127
128// ACPairNode
129
131 std::vector<Pauli> z_string, std::vector<Pauli> x_string, bool z_sign,
132 bool x_sign)
133 : z_string_(z_string),
134 x_string_(x_string),
135 z_sign_(z_sign),
136 x_sign_(x_sign) {
137 if (z_string.empty() || x_string.empty()) {
138 throw GreedyPauliSimpError("ACPairNode cannot have empty strings.");
139 }
142 for (unsigned i = 0; i < z_string_.size(); i++) {
143 CommuteType commute_type =
144 get_pauli_pair_commute_type(z_string_[i], x_string_[i]);
145 commute_type_vec_.push_back(commute_type);
146 if (commute_type == CommuteType::C) {
148 }
149 if (commute_type == CommuteType::A) {
151 }
152 }
153}
154
155unsigned ACPairNode::tqe_cost() const {
156 // for a node with n A pairs and m C pairs
157 // it takes (n-1)/2 TQE gates to convert n-1 A
158 // pairs to C pairs. It then takes n-1+m TQE gates
159 // to convert all the C pairs to I pairs.
160 // total TQEs required is 1.5n - 1.5 + m
161 return static_cast<unsigned>(
163}
164
165int ACPairNode::tqe_cost_increase(const TQE& tqe) const {
166 const TQEType& g = tqe.type;
167 const unsigned& a = tqe.a;
168 const unsigned& b = tqe.b;
169 Pauli z_p0 = z_string_[a];
170 Pauli z_p1 = z_string_[b];
171 Pauli x_p0 = x_string_[a];
172 Pauli x_p1 = x_string_[b];
173 auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP::at({g, z_p0, z_p1});
174 auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP::at({g, x_p0, x_p1});
175 CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0);
176 CommuteType new_b_type = get_pauli_pair_commute_type(new_z_p1, new_x_p1);
177 unsigned old_anti_commutes = (commute_type_vec_[a] == CommuteType::A) +
179 unsigned old_commutes = (commute_type_vec_[a] == CommuteType::C) +
181 unsigned new_anti_commutes =
182 (new_a_type == CommuteType::A) + (new_b_type == CommuteType::A);
183 unsigned new_commutes =
184 (new_a_type == CommuteType::C) + (new_b_type == CommuteType::C);
185 int anti_commute_increase = new_anti_commutes - old_anti_commutes;
186 int commute_increase = new_commutes - old_commutes;
187 return static_cast<int>(1.5 * anti_commute_increase + commute_increase);
188}
189
190void ACPairNode::update(const TQE& tqe) {
191 const TQEType& g = tqe.type;
192 const unsigned& a = tqe.a;
193 const unsigned& b = tqe.b;
194 Pauli z_p0 = z_string_[a];
195 Pauli z_p1 = z_string_[b];
196 Pauli x_p0 = x_string_[a];
197 Pauli x_p1 = x_string_[b];
198 auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP::at({g, z_p0, z_p1});
199 auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP::at({g, x_p0, x_p1});
200 CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0);
201 CommuteType new_b_type = get_pauli_pair_commute_type(new_z_p1, new_x_p1);
202 unsigned old_anti_commutes = (commute_type_vec_[a] == CommuteType::A) +
204 unsigned old_commutes = (commute_type_vec_[a] == CommuteType::C) +
206 unsigned new_anti_commutes =
207 (new_a_type == CommuteType::A) + (new_b_type == CommuteType::A);
208 unsigned new_commutes =
209 (new_a_type == CommuteType::C) + (new_b_type == CommuteType::C);
210 int anti_commute_increase = new_anti_commutes - old_anti_commutes;
211 int commute_increase = new_commutes - old_commutes;
212 n_anti_commute_entries_ += anti_commute_increase;
213 n_commute_entries_ += commute_increase;
214 commute_type_vec_[a] = new_a_type;
215 commute_type_vec_[b] = new_b_type;
216 z_string_[a] = new_z_p0;
217 z_string_[b] = new_z_p1;
218 x_string_[a] = new_x_p0;
219 x_string_[b] = new_x_p1;
220 if (!z_sign) {
221 z_sign_ = !z_sign_;
222 }
223 if (!x_sign) {
224 x_sign_ = !x_sign_;
225 }
226}
227
228void ACPairNode::update(const OpType& sq_cliff, const unsigned& a) {
229 auto [new_z_p, z_sign] = SQ_CLIFF_MAP.at({sq_cliff, z_string_[a]});
230 auto [new_x_p, x_sign] = SQ_CLIFF_MAP.at({sq_cliff, x_string_[a]});
231 z_string_[a] = new_z_p;
232 x_string_[a] = new_x_p;
233 if (!z_sign) {
234 z_sign_ = !z_sign_;
235 }
236 if (!x_sign) {
237 x_sign_ = !x_sign_;
238 }
239}
240
241void ACPairNode::swap(const unsigned& a, const unsigned& b) {
242 std::swap(z_string_[a], z_string_[b]);
243 std::swap(x_string_[a], x_string_[b]);
244 std::swap(commute_type_vec_[a], commute_type_vec_[b]);
245}
246
247std::vector<TQE> ACPairNode::reduction_tqes() const {
248 std::vector<TQE> tqes;
249 // qubits with support
250 std::vector<unsigned> sqs;
251 for (unsigned i = 0; i < commute_type_vec_.size(); i++) {
252 if (commute_type_vec_[i] != CommuteType::I) sqs.push_back(i);
253 }
254 TKET_ASSERT(!sqs.empty());
255 for (unsigned i = 0; i < sqs.size() - 1; i++) {
256 for (unsigned j = i + 1; j < sqs.size(); j++) {
257 std::vector<TQEType> tqe_types;
258 unsigned a = sqs[i];
259 unsigned b = sqs[j];
260 CommuteType ctype0 = commute_type_vec_[a];
261 CommuteType ctype1 = commute_type_vec_[b];
262 if (ctype0 == CommuteType::A) {
263 if (ctype1 == CommuteType::A) {
264 // TQEs that transform a AA pair to CC
265 tqe_types = AA_TO_CC_MAP.at(
266 {z_string_[a], z_string_[b], x_string_[a], x_string_[b]});
267 } else {
268 // TQEs that transform a AC pair to AI
269 tqe_types = AC_TO_AI_MAP.at(
270 {z_string_[a], z_string_[b], x_string_[a], x_string_[b]});
271 }
272 } else {
273 if (ctype1 == CommuteType::A) {
274 // TQEs that transform a CA pair to a IA
275 tqe_types = AC_TO_AI_MAP.at(
276 {z_string_[b], z_string_[a], x_string_[b], x_string_[a]});
277 // flip qubits
278 a = sqs[j];
279 b = sqs[i];
280 } else {
281 // TQEs that transform a CC pair to CI or IC, not always
282 // possible
283 tqe_types = CC_TO_IC_OR_CI_MAP.at(
284 {z_string_[a], z_string_[b], x_string_[a], x_string_[b]});
285 }
286 }
287 for (const TQEType& tt : tqe_types) {
288 tqes.push_back({tt, a, b});
289 }
290 }
291 }
292 return tqes;
293}
294
295std::tuple<unsigned, Pauli, Pauli> ACPairNode::first_support() const {
296 for (unsigned i = 0; i < commute_type_vec_.size(); i++) {
298 return {i, z_string_[i], x_string_[i]};
299 }
300 }
301 // Should be impossible to reach here
302 TKET_ASSERT(false);
303}
304
305// PauliRotation
306PauliRotation::PauliRotation(std::vector<Pauli> string, bool sign, Expr theta)
307 : SingleNode(string, sign), theta_(theta) {}
308
310
312 std::vector<std::tuple<std::vector<Pauli>, bool, Expr>> rotations,
313 std::vector<unsigned> cond_bits, unsigned cond_value)
314 : rotations_(rotations), cond_bits_(cond_bits), cond_value_(cond_value) {
315 total_weight_ = 0;
316 for (const std::tuple<std::vector<Pauli>, bool, Expr>& rot : rotations_) {
318 std::get<0>(rot).size() -
319 std::count(std::get<0>(rot).begin(), std::get<0>(rot).end(), Pauli::I);
320 }
321}
322
323unsigned ConditionalBlock::tqe_cost() const { return total_weight_ - 1; }
324
326 int total_increase = 0;
327 for (const std::tuple<std::vector<Pauli>, bool, Expr>& rot : rotations_) {
328 const TQEType& g = tqe.type;
329 const unsigned& a = tqe.a;
330 const unsigned& b = tqe.b;
331 Pauli p0 = std::get<0>(rot)[a];
332 Pauli p1 = std::get<0>(rot)[b];
333 total_increase += TQE_PAULI_MAP::cost_increase({g, p0, p1});
334 }
335 return total_increase;
336}
337
339 for (std::tuple<std::vector<Pauli>, bool, Expr>& rot : rotations_) {
340 const TQEType& g = tqe.type;
341 const unsigned& a = tqe.a;
342 const unsigned& b = tqe.b;
343 Pauli p0 = std::get<0>(rot)[a];
344 Pauli p1 = std::get<0>(rot)[b];
345 const TQE_PAULI_MAP::Key key{g, p0, p1};
347 continue;
348 }
349 auto [new_p0, new_p1, sign] = TQE_PAULI_MAP::at(key);
350 std::get<0>(rot)[a] = new_p0;
351 std::get<0>(rot)[b] = new_p1;
353 if (!sign) {
354 std::get<1>(rot) = !std::get<1>(rot);
355 }
356 }
357}
358
360 std::vector<std::pair<UnitID, BitType>> bits_info;
361 for (unsigned b : cond_bits_) {
362 bits_info.push_back({Bit(b), BitType::READ});
363 }
364 std::vector<std::vector<Pauli>> strings;
365 for (const std::tuple<std::vector<Pauli>, bool, Expr>& rot : rotations_) {
366 strings.push_back(std::get<0>(rot));
367 }
368 return {strings, bits_info};
369}
370
372 for (const auto& rot : other.rotations()) {
373 rotations_.push_back(rot);
375 std::get<0>(rot).size() -
376 std::count(std::get<0>(rot).begin(), std::get<0>(rot).end(), Pauli::I);
377 }
378}
379
380// PauliPropagation
382 std::vector<Pauli> z_string, std::vector<Pauli> x_string, bool z_sign,
383 bool x_sign, unsigned qubit_index)
384 : ACPairNode(z_string, x_string, z_sign, x_sign),
385 qubit_index_(qubit_index) {}
386
390
391// ClassicalNode
392ClassicalNode::ClassicalNode(std::vector<UnitID> args, Op_ptr op)
393 : args_(args), op_(op) {}
394
396 std::vector<std::pair<UnitID, BitType>> bits_info;
397 for (const UnitID& b : args_) {
398 bits_info.push_back({b, BitType::WRITE});
399 }
400 return {{}, bits_info};
401}
402
403// MidMeasure
404MidMeasure::MidMeasure(std::vector<Pauli> string, bool sign, unsigned bit)
405 : SingleNode(string, sign), bit_(bit) {}
406
410
411// Reset
413 std::vector<Pauli> z_string, std::vector<Pauli> x_string, bool z_sign,
414 bool x_sign)
415 : ACPairNode(z_string, x_string, z_sign, x_sign) {}
416
418 return {{z_string_, x_string_}, {}};
419}
420
421} // namespace GreedyPauliSimp
422
423} // namespace Transforms
424
425} // namespace tket
Location holding a bit.
Definition UnitID.hpp:192
Base class for nodes defined by a pair of anti-commuting Pauli strings.
int tqe_cost_increase(const TQE &tqe) const override
Number of additional TQEs would required to reduce the weight to 1 after the given TQE is applied.
ACPairNode(std::vector< Pauli > z_string, std::vector< Pauli > x_string, bool z_sign, bool x_sign)
Construct a new ACPairNode object.
std::vector< TQE > reduction_tqes() const override
Return all possible TQE gates that will reduce the tqe cost.
void swap(const unsigned &a, const unsigned &b) override
Update the support vector with a SWAP gate.
std::tuple< unsigned, Pauli, Pauli > first_support() const
Return the index and value of the first anti-commute entry.
void update(const TQE &tqe) override
Update the support vector with a TQE gate.
unsigned tqe_cost() const override
Number of TQEs required to reduce the weight to 1.
ClassicalNode(std::vector< UnitID > args, Op_ptr op)
ConditionalBlock(std::vector< std::tuple< std::vector< Pauli >, bool, Expr > > rotations, std::vector< unsigned > cond_bits, unsigned cond_value)
Construct a new Conditional Block object.
std::vector< std::tuple< std::vector< Pauli >, bool, Expr > > rotations_
const std::vector< std::tuple< std::vector< Pauli >, bool, Expr > > & rotations() const
void update(const TQE &tqe) override
Update the all Pauli rotations with the given TQE.
unsigned tqe_cost() const override
Sum of tqe_cost for each Pauli rotation.
int tqe_cost_increase(const TQE &tqe) const override
Sum of tqe_cost for each Pauli rotation after the given TQE is applied.
MidMeasure(std::vector< Pauli > string, bool sign, unsigned bit)
Construct a new Mid Measure object.
virtual void update(const TQE &tqe)=0
virtual void swap(const unsigned &a, const unsigned &b)
PauliPropagation(std::vector< Pauli > z_string, std::vector< Pauli > x_string, bool z_sign, bool x_sign, unsigned qubit_index)
Construct a new PauliPropagation object.
PauliRotation(std::vector< Pauli > string, bool sign, Expr theta)
Construct a new PauliRotation object.
Reset(std::vector< Pauli > z_string, std::vector< Pauli > x_string, bool z_sign, bool x_sign)
Construct a new Reset object.
CommuteInfo get_commute_info() const override
Base class for nodes defined by a single Pauli string.
int tqe_cost_increase(const TQE &tqe) const override
Number of TQEs would required to reduce the weight to 1 after the given TQE is applied.
std::vector< TQE > reduction_tqes() const override
Return all possible TQE gates that will reduce the tqe cost by 1.
void update(const TQE &tqe) override
Update the Pauli string with a TQE gate.
SingleNode(std::vector< Pauli > string, bool sign)
Construct a new SinglePauliNode object.
std::pair< unsigned, Pauli > first_support() const
Return the index and value of the first non-identity.
unsigned tqe_cost() const override
Number of TQEs required to reduce the weight to 1.
Location holding a bit or qubit of information.
Definition UnitID.hpp:68
TQEType
Types of 2-qubit entangled Clifford gates.
CommuteType
The type of a pair of Pauli letters defined by their commutation relation.
Defines tket::DeviceCharacterisation, used in NoiseAwarePlacement and in commute_SQ_gates_through_SWA...
Definition Path.cpp:22
OpType
Named operation types.
Definition OpType.hpp:29
Pauli
Symbols for the Pauli operators (and identity)
SymEngine::Expression Expr
Representation of a phase as a multiple of .
std::shared_ptr< const Op > Op_ptr
Definition OpPtr.hpp:24
Commutation information of a node specified by a list of Pauli strings along with classical READs and...
Struct for 2-qubit entangled Clifford gates.