pecos_qsim/clifford_simulator.rs
1// Copyright 2024 The PECOS Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4// in compliance with the License.You may obtain a copy of the License at
5//
6// https://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software distributed under the License
9// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10// or implied. See the License for the specific language governing permissions and limitations under
11// the License.
12
13use super::quantum_simulator::QuantumSimulator;
14use pecos_core::IndexableElement;
15
16/// A simulator that implements Clifford gates.
17#[expect(clippy::min_ident_chars)]
18pub trait CliffordSimulator<T: IndexableElement>: QuantumSimulator {
19 #[inline]
20 #[must_use]
21 fn new(num_qubits: usize) -> Self
22 where
23 Self: Sized,
24 {
25 <Self as QuantumSimulator>::new(num_qubits)
26 }
27
28 #[inline]
29 fn num_qubits(&self) -> usize {
30 <Self as QuantumSimulator>::num_qubits(self)
31 }
32
33 #[inline]
34 fn reset(&mut self) -> &mut Self {
35 <Self as QuantumSimulator>::reset(self)
36 }
37
38 /// Preparation of the +`X_q` operator.
39 /// # Panics
40 /// Will panic if qubit ids don't convert to usize.
41 #[inline]
42 fn px(&mut self, q: T) -> (bool, bool) {
43 let (meas, deter) = self.mx(q);
44 if meas {
45 self.z(q);
46 }
47
48 (meas, deter)
49 }
50
51 /// Preparation of the -`X_q` operator.
52 /// # Panics
53 /// Will panic if qubit ids don't convert to usize.
54 #[inline]
55 fn pnx(&mut self, q: T) -> (bool, bool) {
56 let (meas, deter) = self.mnx(q);
57 if meas {
58 self.z(q);
59 }
60 (meas, deter)
61 }
62
63 /// Preparation of the +`Y_q` operator.
64 /// # Panics
65 /// Will panic if qubit ids don't convert to usize.
66 #[inline]
67 fn py(&mut self, q: T) -> (bool, bool) {
68 let (meas, deter) = self.my(q);
69 if meas {
70 self.z(q);
71 }
72 // let (meas, deter) = self.pz(q);
73 // self.h5(q);
74 (meas, deter)
75 }
76
77 /// Preparation of the -`Y_q` operator.
78 /// # Panics
79 /// Will panic if qubit ids don't convert to usize.
80 #[inline]
81 fn pny(&mut self, q: T) -> (bool, bool) {
82 let (meas, deter) = self.mny(q);
83 if meas {
84 self.z(q);
85 }
86 // let (meas, deter) = self.pz(q);
87 // self.h6(q);
88 (meas, deter)
89 }
90
91 /// Preparation of the +`Z_q` operator.
92 /// # Panics
93 /// Will panic if qubit ids don't convert to usize.
94 #[inline]
95 fn pz(&mut self, q: T) -> (bool, bool) {
96 let (meas, deter) = self.mz(q);
97 if meas {
98 self.x(q);
99 }
100 (meas, deter)
101 }
102
103 /// Preparation of the -`Z_q` operator.
104 /// # Panics
105 /// Will panic if qubit ids don't convert to usize.
106 #[inline]
107 fn pnz(&mut self, q: T) -> (bool, bool) {
108 let (meas, deter) = self.mnz(q);
109 if meas {
110 self.x(q);
111 }
112 (meas, deter)
113 }
114
115 /// Measurement of the +`X_q` operator.
116 #[inline]
117 fn mx(&mut self, q: T) -> (bool, bool) {
118 // +X -> +Z
119 self.h(q);
120 let (meas, deter) = self.mz(q);
121 // +Z -> +X
122 self.h(q);
123
124 (meas, deter)
125 }
126
127 /// Measurement of the -`X_q` operator.
128 #[inline]
129 fn mnx(&mut self, q: T) -> (bool, bool) {
130 // -X -> +Z
131 self.h(q);
132 self.x(q);
133 let (meas, deter) = self.mz(q);
134 // +Z -> -X
135 self.x(q);
136 self.h(q);
137
138 (meas, deter)
139 }
140
141 /// Measurement of the +`Y_q` operator.
142 /// # Panics
143 /// Will panic if qubit ids don't convert to usize.
144 #[inline]
145 fn my(&mut self, q: T) -> (bool, bool) {
146 // +Y -> +Z
147 self.sx(q);
148 // self.h5(q);
149 let (meas, deter) = self.mz(q);
150 // +Z -> +Y
151 self.sxdg(q);
152 // self.h5(q);
153
154 (meas, deter)
155 }
156
157 /// Measurement of the -`Y_q` operator.
158 /// # Panics
159 /// Will panic if qubit ids don't convert to usize.
160 #[inline]
161 fn mny(&mut self, q: T) -> (bool, bool) {
162 // -Y -> +Z
163 self.sxdg(q);
164 // self.h6(q);
165 let (meas, deter) = self.mz(q);
166 // +Z -> -Y
167 self.sx(q);
168 // self.h6(q);
169
170 (meas, deter)
171 }
172
173 /// Measurement of the +`Z_q` operator.
174 fn mz(&mut self, q: T) -> (bool, bool);
175
176 /// Measurement of the -`Z_q` operator.
177 /// # Panics
178 /// Will panic if qubit ids don't convert to usize.
179 #[inline]
180 fn mnz(&mut self, q: T) -> (bool, bool) {
181 // -Z -> +Z
182 self.x(q);
183 let (meas, deter) = self.mz(q);
184 // +Z -> -Z
185 self.x(q);
186
187 (meas, deter)
188 }
189
190 /// Identity on qubit q. X -> X, Z -> Z
191 #[inline]
192 fn identity(&mut self, _q: T) {}
193
194 /// Pauli X gate. X -> X, Z -> -Z
195 fn x(&mut self, q: T);
196
197 /// Pauli Y gate. X -> -X, Z -> -Z
198 fn y(&mut self, q: T);
199
200 /// Pauli Z gate. X -> -X, Z -> Z
201 fn z(&mut self, q: T);
202
203 /// Sqrt of X gate.
204 /// X -> X
205 /// Z -> -iW = -Y
206 /// W -> -iZ
207 /// Y -> Z
208 #[inline]
209 fn sx(&mut self, q: T) {
210 // X -H-> Z -SZ-> Z -H-> X
211 // Z -H-> X -SZ-> Y -H-> -Y
212 // Y -H-> -Y -SZ-> X -H-> Z
213 self.h(q);
214 self.sz(q);
215 self.h(q);
216 }
217
218 /// Adjoint of Sqrt X gate.
219 /// X -> X
220 /// Z -> iW = Y
221 /// W -> iZ
222 /// Y -> -Z
223 #[inline]
224 fn sxdg(&mut self, q: T) {
225 // X -H-> Z -Z-> Z -SZ-> Z -H-> X
226 // Z -H-> X -Z-> -X -SZ-> -Y -H-> Y
227 // Y -H-> -Y -Z-> Y -SZ-> -X -H-> -Z
228 self.h(q);
229 self.szdg(q);
230 self.h(q);
231 }
232
233 /// Sqrt of Y gate.
234 /// X -> -Z
235 /// Z -> X
236 /// W -> W
237 /// Y -> Y
238 /// # Panics
239 /// Will panic if qubit ids don't convert to usize.
240 #[inline]
241 fn sy(&mut self, q: T) {
242 self.h(q);
243 self.x(q);
244 }
245
246 /// Adjoint of sqrt of Y gate.
247 /// X -> Z
248 /// Z -> -X
249 /// W -> W
250 /// Y -> Y
251 /// # Panics
252 /// Will panic if qubit ids don't convert to usize.
253 #[inline]
254 fn sydg(&mut self, q: T) {
255 self.x(q);
256 self.h(q);
257 }
258
259 /// Sqrt of Z gate. +X -> +Y; +Z -> +Z; +Y -> -X;
260 fn sz(&mut self, q: T);
261
262 /// Adjoint of Sqrt of Z gate. X -> ..., Z -> ...
263 /// X -> -iW = -Y
264 /// Z -> Z
265 /// W -> -iX
266 /// Y -> X
267 #[inline]
268 fn szdg(&mut self, q: T) {
269 // X -Z-> -X -SZ-> -Y
270 // Z -Z-> Z -SZ-> Z
271 // Y -Z-> -Y -SZ-> X
272 self.z(q);
273 self.sz(q);
274 }
275
276 /// Hadamard gate. X -> Z, Z -> X
277 fn h(&mut self, q: T);
278
279 /// X -> -Z, Z -> -X, Y -> -Y
280 #[inline]
281 fn h2(&mut self, q: T) {
282 self.sy(q);
283 self.z(q);
284 }
285
286 /// X -> Y, Z -> -Z, Y -> X
287 #[inline]
288 fn h3(&mut self, q: T) {
289 self.sz(q);
290 self.y(q);
291 }
292
293 /// X -> -Y, Z -> -Z, Y -> -X
294 #[inline]
295 fn h4(&mut self, q: T) {
296 self.sz(q);
297 self.x(q);
298 }
299
300 /// X -> -X, Z -> Y, Y -> Z
301 #[inline]
302 fn h5(&mut self, q: T) {
303 self.sx(q);
304 self.z(q);
305 }
306
307 /// X -> -X, Z -> -Y, Y -> -Z
308 #[inline]
309 fn h6(&mut self, q: T) {
310 self.sx(q);
311 self.y(q);
312 }
313
314 /// X -> Y, Z -> X, Y -> Z
315 #[inline]
316 fn f(&mut self, q: T) {
317 self.sx(q);
318 self.sz(q);
319 }
320
321 /// X -> Z, Z -> Y, Y -> X
322 #[inline]
323 fn fdg(&mut self, q: T) {
324 self.szdg(q);
325 self.sxdg(q);
326 }
327
328 /// X -> -Z, Z -> Y, Y -> -X
329 #[inline]
330 fn f2(&mut self, q: T) {
331 self.sxdg(q);
332 self.sy(q);
333 }
334
335 /// X -> -Y, Z -> -X, Y -> Z
336 #[inline]
337 fn f2dg(&mut self, q: T) {
338 self.sydg(q);
339 self.sx(q);
340 }
341
342 /// X -> Y, Z -> -X, Y -> -Z
343 #[inline]
344 fn f3(&mut self, q: T) {
345 self.sxdg(q);
346 self.sz(q);
347 }
348
349 /// X -> -Z, Z -> -Y, Y -> X
350 #[inline]
351 fn f3dg(&mut self, q: T) {
352 self.szdg(q);
353 self.sx(q);
354 }
355
356 /// X -> Z, Z -> -Y, Y -> -X
357 #[inline]
358 fn f4(&mut self, q: T) {
359 self.sz(q);
360 self.sx(q);
361 }
362
363 /// X -> -Y, Z -> X, Y -> -Z
364 #[inline]
365 fn f4dg(&mut self, q: T) {
366 self.sxdg(q);
367 self.szdg(q);
368 }
369
370 /// Controlled-not gate. IX -> IX, IZ -> ZZ, XI -> XX, ZI -> ZZ
371 fn cx(&mut self, q1: T, q2: T);
372
373 /// CY: +IX -> +ZX; +IZ -> +ZZ; +XI -> -XY; +ZI -> +ZI;
374 #[inline]
375 fn cy(&mut self, q1: T, q2: T) {
376 self.sz(q2);
377 self.cx(q1, q2);
378 self.szdg(q2);
379 }
380
381 /// CZ: +IX -> +ZX; +IZ -> +IZ; +XI -> +XZ; +ZI -> +ZI;
382 #[inline]
383 fn cz(&mut self, q1: T, q2: T) {
384 self.h(q2);
385 self.cx(q1, q2);
386 self.h(q2);
387 }
388
389 /// SXX: XI -> XI
390 /// IX -> IX
391 /// ZI -> -YX
392 /// IZ -> -XY
393 #[inline]
394 fn sxx(&mut self, q1: T, q2: T) {
395 self.sx(q1);
396 self.sx(q2);
397 self.sydg(q1);
398 self.cx(q1, q2);
399 self.sy(q1);
400 }
401
402 /// `SXXdg`: XI -> XI
403 /// IX -> IX
404 /// ZI -> YX
405 /// IZ -> XY
406 #[inline]
407 fn sxxdg(&mut self, q1: T, q2: T) {
408 self.x(q1);
409 self.x(q2);
410 self.sxx(q1, q2);
411 }
412
413 /// SYY: XI -> -ZY
414 /// IX -> -YZ
415 /// ZI -> XY
416 /// IZ -> YX
417 #[inline]
418 fn syy(&mut self, q1: T, q2: T) {
419 self.szdg(q1);
420 self.szdg(q2);
421 self.sxx(q1, q2);
422 self.sz(q1);
423 self.sz(q2);
424 }
425
426 /// `SYYdg`: XI -> ZY
427 /// IX -> YZ
428 /// ZI -> -XY
429 /// IZ -> -YX
430 #[inline]
431 fn syydg(&mut self, q1: T, q2: T) {
432 self.y(q1);
433 self.y(q2);
434 self.syy(q1, q2);
435 }
436
437 /// SZZ: +IX -> +ZY;
438 /// +IZ -> +IZ;
439 /// +XI -> +YZ;
440 /// +ZI -> +ZI;
441 #[inline]
442 fn szz(&mut self, q1: T, q2: T) {
443 self.sydg(q1);
444 self.sydg(q2);
445 self.sxx(q1, q2);
446 self.sy(q1);
447 self.sy(q2);
448 }
449
450 /// `SZZdg`: +IX -> -ZY;
451 /// +IZ -> +IZ;
452 /// +XI -> -ZY;
453 /// +ZI -> +ZI;
454 #[inline]
455 fn szzdg(&mut self, q1: T, q2: T) {
456 self.z(q1);
457 self.z(q2);
458 self.szz(q1, q2);
459 // self.sy(q1);
460 // self.sy(q2);
461 // self.sxxdg(q1, q2);
462 // self.sydg(q1);
463 // self.sydg(q2);
464 }
465
466 /// SWAP: +IX -> XI;
467 /// +IZ -> ZI;
468 /// +XI -> IX;
469 /// +ZI -> IZ;
470 #[inline]
471 fn swap(&mut self, q1: T, q2: T) {
472 self.cx(q1, q2);
473 self.cx(q2, q1);
474 self.cx(q1, q2);
475 }
476
477 /// G2: +XI -> +IX
478 /// +IX -> +XI
479 /// +ZI -> +XZ
480 /// +IZ -> +ZX
481 #[inline]
482 fn g2(&mut self, q1: T, q2: T) {
483 self.cz(q1, q2);
484 self.h(q1);
485 self.h(q2);
486 self.cz(q1, q2);
487 }
488}