quantrs2_ml/torchquantum/gates/
three_qubit.rs

1//! Three-qubit gate implementations
2//!
3//! This module provides all three-qubit gates including:
4//! - Toffoli (CCX) gate
5//! - CSWAP (Fredkin) gate
6//! - CCZ gate
7
8use super::super::{
9    CType, NParamsEnum, OpHistoryEntry, TQDevice, TQModule, TQOperator, TQParameter, WiresEnum,
10};
11use crate::error::{MLError, Result};
12use scirs2_core::ndarray::Array2;
13
14/// CSWAP gate - Controlled SWAP (Fredkin gate)
15#[derive(Debug, Clone)]
16pub struct TQCSWAP {
17    inverse: bool,
18    static_mode: bool,
19}
20
21impl TQCSWAP {
22    pub fn new() -> Self {
23        Self {
24            inverse: false,
25            static_mode: false,
26        }
27    }
28}
29
30impl Default for TQCSWAP {
31    fn default() -> Self {
32        Self::new()
33    }
34}
35
36impl TQModule for TQCSWAP {
37    fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
38        Err(MLError::InvalidConfiguration(
39            "Use apply() instead of forward() for operators".to_string(),
40        ))
41    }
42
43    fn parameters(&self) -> Vec<TQParameter> {
44        Vec::new()
45    }
46
47    fn n_wires(&self) -> Option<usize> {
48        Some(3)
49    }
50
51    fn set_n_wires(&mut self, _n_wires: usize) {}
52
53    fn is_static_mode(&self) -> bool {
54        self.static_mode
55    }
56
57    fn static_on(&mut self) {
58        self.static_mode = true;
59    }
60
61    fn static_off(&mut self) {
62        self.static_mode = false;
63    }
64
65    fn name(&self) -> &str {
66        "CSWAP"
67    }
68}
69
70impl TQOperator for TQCSWAP {
71    fn num_wires(&self) -> WiresEnum {
72        WiresEnum::Fixed(3)
73    }
74
75    fn num_params(&self) -> NParamsEnum {
76        NParamsEnum::Fixed(0)
77    }
78
79    fn get_matrix(&self, _params: Option<&[f64]>) -> Array2<CType> {
80        // CSWAP (Fredkin) 8x8 matrix
81        // swaps qubits 1 and 2 when qubit 0 is |1>
82        let mut matrix = Array2::eye(8).mapv(|x| CType::new(x, 0.0));
83        // Swap |101> <-> |110> (indices 5 <-> 6)
84        matrix[[5, 5]] = CType::new(0.0, 0.0);
85        matrix[[5, 6]] = CType::new(1.0, 0.0);
86        matrix[[6, 5]] = CType::new(1.0, 0.0);
87        matrix[[6, 6]] = CType::new(0.0, 0.0);
88        matrix
89    }
90
91    fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
92        self.apply_with_params(qdev, wires, None)
93    }
94
95    fn apply_with_params(
96        &mut self,
97        qdev: &mut TQDevice,
98        wires: &[usize],
99        _params: Option<&[f64]>,
100    ) -> Result<()> {
101        if wires.len() < 3 {
102            return Err(MLError::InvalidConfiguration(
103                "CSWAP gate requires exactly 3 wires".to_string(),
104            ));
105        }
106        let matrix = self.get_matrix(None);
107        qdev.apply_multi_qubit_gate(wires, &matrix)?;
108
109        if qdev.record_op {
110            qdev.record_operation(OpHistoryEntry {
111                name: "cswap".to_string(),
112                wires: wires.to_vec(),
113                params: None,
114                inverse: self.inverse,
115                trainable: false,
116            });
117        }
118
119        Ok(())
120    }
121
122    fn has_params(&self) -> bool {
123        false
124    }
125
126    fn trainable(&self) -> bool {
127        false
128    }
129
130    fn inverse(&self) -> bool {
131        self.inverse
132    }
133
134    fn set_inverse(&mut self, inverse: bool) {
135        self.inverse = inverse;
136    }
137}
138
139/// Toffoli gate (CCX) - Controlled-Controlled-X gate
140#[derive(Debug, Clone)]
141pub struct TQToffoli {
142    inverse: bool,
143    static_mode: bool,
144}
145
146impl TQToffoli {
147    pub fn new() -> Self {
148        Self {
149            inverse: false,
150            static_mode: false,
151        }
152    }
153}
154
155impl Default for TQToffoli {
156    fn default() -> Self {
157        Self::new()
158    }
159}
160
161impl TQModule for TQToffoli {
162    fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
163        Err(MLError::InvalidConfiguration(
164            "Use apply() instead of forward() for operators".to_string(),
165        ))
166    }
167
168    fn parameters(&self) -> Vec<TQParameter> {
169        Vec::new()
170    }
171
172    fn n_wires(&self) -> Option<usize> {
173        Some(3)
174    }
175
176    fn set_n_wires(&mut self, _n_wires: usize) {}
177
178    fn is_static_mode(&self) -> bool {
179        self.static_mode
180    }
181
182    fn static_on(&mut self) {
183        self.static_mode = true;
184    }
185
186    fn static_off(&mut self) {
187        self.static_mode = false;
188    }
189
190    fn name(&self) -> &str {
191        "Toffoli"
192    }
193}
194
195impl TQOperator for TQToffoli {
196    fn num_wires(&self) -> WiresEnum {
197        WiresEnum::Fixed(3)
198    }
199
200    fn num_params(&self) -> NParamsEnum {
201        NParamsEnum::Fixed(0)
202    }
203
204    fn get_matrix(&self, _params: Option<&[f64]>) -> Array2<CType> {
205        // Toffoli (CCX) 8x8 matrix
206        // Flips qubit 2 when qubits 0 and 1 are both |1>
207        let mut matrix = Array2::eye(8).mapv(|x| CType::new(x, 0.0));
208        // Swap |110> <-> |111> (indices 6 <-> 7)
209        matrix[[6, 6]] = CType::new(0.0, 0.0);
210        matrix[[6, 7]] = CType::new(1.0, 0.0);
211        matrix[[7, 6]] = CType::new(1.0, 0.0);
212        matrix[[7, 7]] = CType::new(0.0, 0.0);
213        matrix
214    }
215
216    fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
217        self.apply_with_params(qdev, wires, None)
218    }
219
220    fn apply_with_params(
221        &mut self,
222        qdev: &mut TQDevice,
223        wires: &[usize],
224        _params: Option<&[f64]>,
225    ) -> Result<()> {
226        if wires.len() < 3 {
227            return Err(MLError::InvalidConfiguration(
228                "Toffoli gate requires exactly 3 wires".to_string(),
229            ));
230        }
231        let matrix = self.get_matrix(None);
232        qdev.apply_multi_qubit_gate(wires, &matrix)?;
233
234        if qdev.record_op {
235            qdev.record_operation(OpHistoryEntry {
236                name: "toffoli".to_string(),
237                wires: wires.to_vec(),
238                params: None,
239                inverse: self.inverse,
240                trainable: false,
241            });
242        }
243
244        Ok(())
245    }
246
247    fn has_params(&self) -> bool {
248        false
249    }
250
251    fn trainable(&self) -> bool {
252        false
253    }
254
255    fn inverse(&self) -> bool {
256        self.inverse
257    }
258
259    fn set_inverse(&mut self, inverse: bool) {
260        self.inverse = inverse;
261    }
262}
263
264/// CCZ gate - Controlled-Controlled-Z gate
265#[derive(Debug, Clone)]
266pub struct TQCCZ {
267    inverse: bool,
268    static_mode: bool,
269}
270
271impl TQCCZ {
272    pub fn new() -> Self {
273        Self {
274            inverse: false,
275            static_mode: false,
276        }
277    }
278}
279
280impl Default for TQCCZ {
281    fn default() -> Self {
282        Self::new()
283    }
284}
285
286impl TQModule for TQCCZ {
287    fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
288        Err(MLError::InvalidConfiguration(
289            "Use apply() instead of forward() for operators".to_string(),
290        ))
291    }
292
293    fn parameters(&self) -> Vec<TQParameter> {
294        Vec::new()
295    }
296
297    fn n_wires(&self) -> Option<usize> {
298        Some(3)
299    }
300
301    fn set_n_wires(&mut self, _n_wires: usize) {}
302
303    fn is_static_mode(&self) -> bool {
304        self.static_mode
305    }
306
307    fn static_on(&mut self) {
308        self.static_mode = true;
309    }
310
311    fn static_off(&mut self) {
312        self.static_mode = false;
313    }
314
315    fn name(&self) -> &str {
316        "CCZ"
317    }
318}
319
320impl TQOperator for TQCCZ {
321    fn num_wires(&self) -> WiresEnum {
322        WiresEnum::Fixed(3)
323    }
324
325    fn num_params(&self) -> NParamsEnum {
326        NParamsEnum::Fixed(0)
327    }
328
329    fn get_matrix(&self, _params: Option<&[f64]>) -> Array2<CType> {
330        // CCZ 8x8 matrix - applies Z when both controls are |1>
331        let mut matrix = Array2::eye(8).mapv(|x| CType::new(x, 0.0));
332        // Apply -1 phase to |111> (index 7)
333        matrix[[7, 7]] = CType::new(-1.0, 0.0);
334        matrix
335    }
336
337    fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
338        self.apply_with_params(qdev, wires, None)
339    }
340
341    fn apply_with_params(
342        &mut self,
343        qdev: &mut TQDevice,
344        wires: &[usize],
345        _params: Option<&[f64]>,
346    ) -> Result<()> {
347        if wires.len() < 3 {
348            return Err(MLError::InvalidConfiguration(
349                "CCZ gate requires exactly 3 wires".to_string(),
350            ));
351        }
352        let matrix = self.get_matrix(None);
353        qdev.apply_multi_qubit_gate(wires, &matrix)?;
354
355        if qdev.record_op {
356            qdev.record_operation(OpHistoryEntry {
357                name: "ccz".to_string(),
358                wires: wires.to_vec(),
359                params: None,
360                inverse: self.inverse,
361                trainable: false,
362            });
363        }
364
365        Ok(())
366    }
367
368    fn has_params(&self) -> bool {
369        false
370    }
371
372    fn trainable(&self) -> bool {
373        false
374    }
375
376    fn inverse(&self) -> bool {
377        self.inverse
378    }
379
380    fn set_inverse(&mut self, inverse: bool) {
381        self.inverse = inverse;
382    }
383}
384
385/// Type aliases for TorchQuantum compatibility
386pub type TQCCX = TQToffoli;
387pub type TQFredkin = TQCSWAP;