quantrs2_core/
controlled.rs

1//! Controlled gate operations
2//!
3//! This module provides support for creating controlled versions of arbitrary quantum gates,
4//! including multi-controlled gates and phase-controlled operations.
5
6use crate::error::{QuantRS2Error, QuantRS2Result};
7use crate::gate::GateOp;
8use crate::qubit::QubitId;
9use num_complex::Complex64;
10use std::any::Any;
11use std::fmt::Debug;
12
13/// A controlled gate wrapper that adds control qubits to any gate
14#[derive(Debug)]
15pub struct ControlledGate {
16    /// The control qubits
17    controls: Vec<QubitId>,
18    /// The base gate to be controlled
19    base_gate: Box<dyn GateOp>,
20    /// Phase to apply when all controls are |1⟩
21    control_phase: Complex64,
22}
23
24impl ControlledGate {
25    /// Create a new controlled gate
26    pub fn new(controls: Vec<QubitId>, base_gate: Box<dyn GateOp>) -> Self {
27        Self {
28            controls,
29            base_gate,
30            control_phase: Complex64::new(1.0, 0.0),
31        }
32    }
33
34    /// Create a new phase-controlled gate
35    pub fn with_phase(
36        controls: Vec<QubitId>,
37        base_gate: Box<dyn GateOp>,
38        phase: Complex64,
39    ) -> Self {
40        Self {
41            controls,
42            base_gate,
43            control_phase: phase,
44        }
45    }
46
47    /// Get the control qubits
48    pub fn controls(&self) -> &[QubitId] {
49        &self.controls
50    }
51
52    /// Get the target qubits
53    pub fn targets(&self) -> Vec<QubitId> {
54        self.base_gate.qubits()
55    }
56
57    /// Check if a qubit is a control
58    pub fn is_control(&self, qubit: QubitId) -> bool {
59        self.controls.contains(&qubit)
60    }
61
62    /// Check if a qubit is a target
63    pub fn is_target(&self, qubit: QubitId) -> bool {
64        self.base_gate.qubits().contains(&qubit)
65    }
66}
67
68impl GateOp for ControlledGate {
69    fn name(&self) -> &'static str {
70        match self.controls.len() {
71            1 => "C",
72            2 => "CC",
73            _ => "Multi-C",
74        }
75    }
76
77    fn qubits(&self) -> Vec<QubitId> {
78        let mut qubits = self.controls.clone();
79        qubits.extend(self.base_gate.qubits());
80        qubits
81    }
82
83    fn is_parameterized(&self) -> bool {
84        self.base_gate.is_parameterized()
85    }
86
87    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
88        let base_matrix = self.base_gate.matrix()?;
89        let base_dim = (base_matrix.len() as f64).sqrt() as usize;
90
91        if base_dim * base_dim != base_matrix.len() {
92            return Err(QuantRS2Error::InvalidInput(
93                "Base gate matrix is not square".to_string(),
94            ));
95        }
96
97        let num_controls = self.controls.len();
98        let control_dim = 1 << num_controls;
99        let total_dim = control_dim * base_dim;
100
101        // Create identity matrix of appropriate size
102        let mut matrix = vec![Complex64::new(0.0, 0.0); total_dim * total_dim];
103        for i in 0..total_dim {
104            matrix[i * total_dim + i] = Complex64::new(1.0, 0.0);
105        }
106
107        // Apply base gate only when all controls are |1⟩
108        let control_mask = control_dim - 1; // All controls = 1
109
110        for i in 0..base_dim {
111            for j in 0..base_dim {
112                let row = control_mask * base_dim + i;
113                let col = control_mask * base_dim + j;
114                matrix[row * total_dim + col] = self.control_phase * base_matrix[i * base_dim + j];
115            }
116        }
117
118        Ok(matrix)
119    }
120
121    fn as_any(&self) -> &dyn Any {
122        self
123    }
124
125    fn clone_gate(&self) -> Box<dyn GateOp> {
126        Box::new(Self {
127            controls: self.controls.clone(),
128            base_gate: self.base_gate.clone(),
129            control_phase: self.control_phase,
130        })
131    }
132}
133
134/// Multi-controlled gate with optimizations for common patterns
135#[derive(Debug)]
136pub struct MultiControlledGate {
137    /// Positive controls (triggered on |1⟩)
138    positive_controls: Vec<QubitId>,
139    /// Negative controls (triggered on |0⟩)  
140    negative_controls: Vec<QubitId>,
141    /// The base gate
142    base_gate: Box<dyn GateOp>,
143}
144
145impl MultiControlledGate {
146    /// Create a new multi-controlled gate
147    pub fn new(
148        positive_controls: Vec<QubitId>,
149        negative_controls: Vec<QubitId>,
150        base_gate: Box<dyn GateOp>,
151    ) -> Self {
152        Self {
153            positive_controls,
154            negative_controls,
155            base_gate,
156        }
157    }
158
159    /// Create with only positive controls
160    pub fn positive(controls: Vec<QubitId>, base_gate: Box<dyn GateOp>) -> Self {
161        Self {
162            positive_controls: controls,
163            negative_controls: vec![],
164            base_gate,
165        }
166    }
167
168    /// Get total number of control qubits
169    pub fn num_controls(&self) -> usize {
170        self.positive_controls.len() + self.negative_controls.len()
171    }
172}
173
174impl GateOp for MultiControlledGate {
175    fn name(&self) -> &'static str {
176        match self.num_controls() {
177            1 => "C",
178            2 => "CC",
179            3 => "CCC",
180            _ => "Multi-C",
181        }
182    }
183
184    fn qubits(&self) -> Vec<QubitId> {
185        let mut qubits = self.positive_controls.clone();
186        qubits.extend(&self.negative_controls);
187        qubits.extend(self.base_gate.qubits());
188        qubits
189    }
190
191    fn is_parameterized(&self) -> bool {
192        self.base_gate.is_parameterized()
193    }
194
195    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
196        let base_matrix = self.base_gate.matrix()?;
197        let base_dim = (base_matrix.len() as f64).sqrt() as usize;
198
199        let num_controls = self.num_controls();
200        let control_dim = 1 << num_controls;
201        let total_dim = control_dim * base_dim;
202
203        // Create identity matrix
204        let mut matrix = vec![Complex64::new(0.0, 0.0); total_dim * total_dim];
205        for i in 0..total_dim {
206            matrix[i * total_dim + i] = Complex64::new(1.0, 0.0);
207        }
208
209        // Determine the control pattern
210        let mut _control_pattern = 0usize;
211        for (i, _) in self.positive_controls.iter().enumerate() {
212            _control_pattern |= 1 << i;
213        }
214        // Negative controls start after positive controls
215        let neg_offset = self.positive_controls.len();
216
217        // Apply base gate only when control pattern matches
218        for ctrl_state in 0..control_dim {
219            let mut matches = true;
220
221            // Check positive controls
222            for (i, _) in self.positive_controls.iter().enumerate() {
223                if (ctrl_state >> i) & 1 != 1 {
224                    matches = false;
225                    break;
226                }
227            }
228
229            // Check negative controls
230            if matches {
231                for (i, _) in self.negative_controls.iter().enumerate() {
232                    if (ctrl_state >> (neg_offset + i)) & 1 != 0 {
233                        matches = false;
234                        break;
235                    }
236                }
237            }
238
239            if matches {
240                // Apply base gate to this control subspace
241                for i in 0..base_dim {
242                    for j in 0..base_dim {
243                        let row = ctrl_state * base_dim + i;
244                        let col = ctrl_state * base_dim + j;
245                        matrix[row * total_dim + col] = base_matrix[i * base_dim + j];
246                    }
247                }
248            }
249        }
250
251        Ok(matrix)
252    }
253
254    fn as_any(&self) -> &dyn Any {
255        self
256    }
257
258    fn clone_gate(&self) -> Box<dyn GateOp> {
259        Box::new(Self {
260            positive_controls: self.positive_controls.clone(),
261            negative_controls: self.negative_controls.clone(),
262            base_gate: self.base_gate.clone(),
263        })
264    }
265}
266
267/// Optimized Toffoli (CCNOT) gate
268#[derive(Debug, Clone, Copy)]
269pub struct ToffoliGate {
270    control1: QubitId,
271    control2: QubitId,
272    target: QubitId,
273}
274
275impl ToffoliGate {
276    /// Create a new Toffoli gate
277    pub fn new(control1: QubitId, control2: QubitId, target: QubitId) -> Self {
278        Self {
279            control1,
280            control2,
281            target,
282        }
283    }
284}
285
286impl GateOp for ToffoliGate {
287    fn name(&self) -> &'static str {
288        "Toffoli"
289    }
290
291    fn qubits(&self) -> Vec<QubitId> {
292        vec![self.control1, self.control2, self.target]
293    }
294
295    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
296        // Toffoli matrix in computational basis
297        let mut matrix = vec![Complex64::new(0.0, 0.0); 64]; // 8x8
298
299        // Identity for most basis states
300        for i in 0..8 {
301            matrix[i * 8 + i] = Complex64::new(1.0, 0.0);
302        }
303
304        // Swap |110⟩ and |111⟩
305        matrix[6 * 8 + 6] = Complex64::new(0.0, 0.0);
306        matrix[7 * 8 + 7] = Complex64::new(0.0, 0.0);
307        matrix[6 * 8 + 7] = Complex64::new(1.0, 0.0);
308        matrix[7 * 8 + 6] = Complex64::new(1.0, 0.0);
309
310        Ok(matrix)
311    }
312
313    fn as_any(&self) -> &dyn Any {
314        self
315    }
316
317    fn clone_gate(&self) -> Box<dyn GateOp> {
318        Box::new(self.clone())
319    }
320}
321
322/// Optimized Fredkin (CSWAP) gate
323#[derive(Debug, Clone, Copy)]
324pub struct FredkinGate {
325    control: QubitId,
326    target1: QubitId,
327    target2: QubitId,
328}
329
330impl FredkinGate {
331    /// Create a new Fredkin gate
332    pub fn new(control: QubitId, target1: QubitId, target2: QubitId) -> Self {
333        Self {
334            control,
335            target1,
336            target2,
337        }
338    }
339}
340
341impl GateOp for FredkinGate {
342    fn name(&self) -> &'static str {
343        "Fredkin"
344    }
345
346    fn qubits(&self) -> Vec<QubitId> {
347        vec![self.control, self.target1, self.target2]
348    }
349
350    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
351        // Fredkin matrix in computational basis
352        let mut matrix = vec![Complex64::new(0.0, 0.0); 64]; // 8x8
353
354        // Identity for most basis states
355        for i in 0..8 {
356            matrix[i * 8 + i] = Complex64::new(1.0, 0.0);
357        }
358
359        // Swap when control is |1⟩
360        // |101⟩ <-> |110⟩
361        matrix[5 * 8 + 5] = Complex64::new(0.0, 0.0);
362        matrix[6 * 8 + 6] = Complex64::new(0.0, 0.0);
363        matrix[5 * 8 + 6] = Complex64::new(1.0, 0.0);
364        matrix[6 * 8 + 5] = Complex64::new(1.0, 0.0);
365
366        Ok(matrix)
367    }
368
369    fn as_any(&self) -> &dyn Any {
370        self
371    }
372
373    fn clone_gate(&self) -> Box<dyn GateOp> {
374        Box::new(self.clone())
375    }
376}
377
378/// Helper function to create controlled version of any gate
379pub fn make_controlled<G: GateOp + 'static>(controls: Vec<QubitId>, gate: G) -> ControlledGate {
380    ControlledGate::new(controls, Box::new(gate))
381}
382
383/// Helper function to create multi-controlled version with mixed controls
384pub fn make_multi_controlled<G: GateOp + 'static>(
385    positive_controls: Vec<QubitId>,
386    negative_controls: Vec<QubitId>,
387    gate: G,
388) -> MultiControlledGate {
389    MultiControlledGate::new(positive_controls, negative_controls, Box::new(gate))
390}
391
392#[cfg(test)]
393mod tests {
394    use super::*;
395    use crate::gate::single::PauliX;
396
397    #[test]
398    fn test_controlled_x_gate() {
399        let x = PauliX { target: QubitId(1) };
400        let cx = make_controlled(vec![QubitId(0)], x);
401
402        assert_eq!(cx.name(), "C");
403        assert_eq!(cx.qubits(), vec![QubitId(0), QubitId(1)]);
404
405        let matrix = cx.matrix().unwrap();
406        assert_eq!(matrix.len(), 16); // 4x4 matrix
407
408        // Check CNOT matrix structure
409        assert_eq!(matrix[0], Complex64::new(1.0, 0.0)); // |00⟩ -> |00⟩
410        assert_eq!(matrix[5], Complex64::new(1.0, 0.0)); // |01⟩ -> |01⟩
411        assert_eq!(matrix[11], Complex64::new(1.0, 0.0)); // |10⟩ -> |11⟩
412        assert_eq!(matrix[14], Complex64::new(1.0, 0.0)); // |11⟩ -> |10⟩
413    }
414
415    #[test]
416    fn test_toffoli_gate() {
417        let toffoli = ToffoliGate::new(QubitId(0), QubitId(1), QubitId(2));
418
419        assert_eq!(toffoli.name(), "Toffoli");
420        assert_eq!(toffoli.qubits().len(), 3);
421
422        let matrix = toffoli.matrix().unwrap();
423        assert_eq!(matrix.len(), 64); // 8x8 matrix
424    }
425}