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}