quantrs2_core/qml/
layers.rs

1//! Common quantum machine learning layers
2//!
3//! This module provides implementations of common QML layers including
4//! rotation layers, entangling layers, and composite layers.
5
6use super::{create_entangling_gates, EntanglementPattern, QMLLayer};
7use crate::{
8    error::{QuantRS2Error, QuantRS2Result},
9    gate::GateOp,
10    parametric::{ParametricRotationX, ParametricRotationY, ParametricRotationZ},
11    qubit::QubitId,
12};
13use ndarray::Array1;
14use num_complex::Complex64;
15use std::f64::consts::PI;
16
17// Parameter type for QML
18#[derive(Debug, Clone)]
19pub struct Parameter {
20    pub name: String,
21    pub value: f64,
22    pub bounds: Option<(f64, f64)>,
23}
24
25// Simple wrapper types for the QML module
26type RXGate = ParametricRotationX;
27type RYGate = ParametricRotationY;
28type RZGate = ParametricRotationZ;
29
30// Simple CNOT gate for QML usage
31#[derive(Debug, Clone, Copy)]
32struct CNOT {
33    control: QubitId,
34    target: QubitId,
35}
36
37impl GateOp for CNOT {
38    fn name(&self) -> &'static str {
39        "CNOT"
40    }
41
42    fn qubits(&self) -> Vec<QubitId> {
43        vec![self.control, self.target]
44    }
45
46    fn matrix(&self) -> crate::error::QuantRS2Result<Vec<Complex64>> {
47        Ok(vec![
48            Complex64::new(1.0, 0.0),
49            Complex64::new(0.0, 0.0),
50            Complex64::new(0.0, 0.0),
51            Complex64::new(0.0, 0.0),
52            Complex64::new(0.0, 0.0),
53            Complex64::new(1.0, 0.0),
54            Complex64::new(0.0, 0.0),
55            Complex64::new(0.0, 0.0),
56            Complex64::new(0.0, 0.0),
57            Complex64::new(0.0, 0.0),
58            Complex64::new(0.0, 0.0),
59            Complex64::new(1.0, 0.0),
60            Complex64::new(0.0, 0.0),
61            Complex64::new(0.0, 0.0),
62            Complex64::new(1.0, 0.0),
63            Complex64::new(0.0, 0.0),
64        ])
65    }
66
67    fn as_any(&self) -> &dyn std::any::Any {
68        self
69    }
70
71    fn clone_gate(&self) -> Box<dyn GateOp> {
72        Box::new(*self)
73    }
74}
75
76/// A layer of rotation gates on all qubits
77#[derive(Debug, Clone)]
78pub struct RotationLayer {
79    /// Number of qubits
80    num_qubits: usize,
81    /// Rotation axes (X, Y, or Z for each qubit)
82    axes: Vec<char>,
83    /// Parameters for each rotation
84    parameters: Vec<Parameter>,
85    /// Layer name
86    name: String,
87}
88
89impl RotationLayer {
90    /// Create a new rotation layer
91    pub fn new(num_qubits: usize, axes: Vec<char>) -> QuantRS2Result<Self> {
92        if axes.len() != num_qubits {
93            return Err(QuantRS2Error::InvalidInput(format!(
94                "Expected {} axes, got {}",
95                num_qubits,
96                axes.len()
97            )));
98        }
99
100        for &axis in &axes {
101            if !['X', 'Y', 'Z'].contains(&axis) {
102                return Err(QuantRS2Error::InvalidInput(format!(
103                    "Invalid rotation axis: {}",
104                    axis
105                )));
106            }
107        }
108
109        let parameters = (0..num_qubits)
110            .map(|i| Parameter {
111                name: format!("rot_{}_{}", axes[i], i),
112                value: 0.0,
113                bounds: Some((-2.0 * PI, 2.0 * PI)),
114            })
115            .collect();
116
117        let name = format!("RotationLayer_{}", axes.iter().collect::<String>());
118
119        Ok(Self {
120            num_qubits,
121            axes,
122            parameters,
123            name,
124        })
125    }
126
127    /// Create a layer with all rotations on the same axis
128    pub fn uniform(num_qubits: usize, axis: char) -> QuantRS2Result<Self> {
129        Self::new(num_qubits, vec![axis; num_qubits])
130    }
131}
132
133impl QMLLayer for RotationLayer {
134    fn num_qubits(&self) -> usize {
135        self.num_qubits
136    }
137
138    fn parameters(&self) -> &[Parameter] {
139        &self.parameters
140    }
141
142    fn parameters_mut(&mut self) -> &mut [Parameter] {
143        &mut self.parameters
144    }
145
146    fn gates(&self) -> Vec<Box<dyn GateOp>> {
147        self.parameters
148            .iter()
149            .enumerate()
150            .map(|(i, param)| {
151                let qubit = QubitId(i as u32);
152                let gate: Box<dyn GateOp> = match self.axes[i] {
153                    'X' => Box::new(ParametricRotationX::new(qubit, param.value)),
154                    'Y' => Box::new(ParametricRotationY::new(qubit, param.value)),
155                    'Z' => Box::new(ParametricRotationZ::new(qubit, param.value)),
156                    _ => unreachable!(),
157                };
158                gate
159            })
160            .collect()
161    }
162
163    fn compute_gradients(
164        &self,
165        _state: &Array1<Complex64>,
166        _loss_gradient: &Array1<Complex64>,
167    ) -> QuantRS2Result<Vec<f64>> {
168        // Use parameter shift rule for each parameter
169        // This is a placeholder implementation
170        let gradients = vec![0.0; self.parameters.len()];
171        Ok(gradients)
172    }
173
174    fn name(&self) -> &str {
175        &self.name
176    }
177}
178
179/// A layer of entangling gates
180#[derive(Debug, Clone)]
181pub struct EntanglingLayer {
182    /// Number of qubits
183    num_qubits: usize,
184    /// Entanglement pattern
185    pattern: EntanglementPattern,
186    /// Parameters for parameterized entangling gates (if any)
187    parameters: Vec<Parameter>,
188    /// Whether to use parameterized gates
189    parameterized: bool,
190    /// Layer name
191    name: String,
192}
193
194impl EntanglingLayer {
195    /// Create a new entangling layer with CNOT gates
196    pub fn new(num_qubits: usize, pattern: EntanglementPattern) -> Self {
197        let name = format!("EntanglingLayer_{:?}", pattern);
198
199        Self {
200            num_qubits,
201            pattern,
202            parameters: vec![],
203            parameterized: false,
204            name,
205        }
206    }
207
208    /// Create a parameterized entangling layer (e.g., with CRZ gates)
209    pub fn parameterized(num_qubits: usize, pattern: EntanglementPattern) -> Self {
210        let pairs = create_entangling_gates(num_qubits, pattern);
211        let parameters = pairs
212            .iter()
213            .enumerate()
214            .map(|(_i, (ctrl, tgt))| Parameter {
215                name: format!("entangle_{}_{}", ctrl.0, tgt.0),
216                value: 0.0,
217                bounds: Some((-PI, PI)),
218            })
219            .collect();
220
221        let name = format!("ParameterizedEntanglingLayer_{:?}", pattern);
222
223        Self {
224            num_qubits,
225            pattern,
226            parameters,
227            parameterized: true,
228            name,
229        }
230    }
231}
232
233impl QMLLayer for EntanglingLayer {
234    fn num_qubits(&self) -> usize {
235        self.num_qubits
236    }
237
238    fn parameters(&self) -> &[Parameter] {
239        &self.parameters
240    }
241
242    fn parameters_mut(&mut self) -> &mut [Parameter] {
243        &mut self.parameters
244    }
245
246    fn gates(&self) -> Vec<Box<dyn GateOp>> {
247        let pairs = create_entangling_gates(self.num_qubits, self.pattern);
248
249        if self.parameterized {
250            // Use parameterized entangling gates (CRZ)
251            pairs
252                .iter()
253                .zip(self.parameters.iter())
254                .map(|((ctrl, tgt), _param)| {
255                    // For now, use CNOT - would implement CRZ
256                    Box::new(CNOT {
257                        control: *ctrl,
258                        target: *tgt,
259                    }) as Box<dyn GateOp>
260                })
261                .collect()
262        } else {
263            // Use fixed CNOT gates
264            pairs
265                .iter()
266                .map(|(ctrl, tgt)| {
267                    Box::new(CNOT {
268                        control: *ctrl,
269                        target: *tgt,
270                    }) as Box<dyn GateOp>
271                })
272                .collect()
273        }
274    }
275
276    fn compute_gradients(
277        &self,
278        _state: &Array1<Complex64>,
279        _loss_gradient: &Array1<Complex64>,
280    ) -> QuantRS2Result<Vec<f64>> {
281        if self.parameterized {
282            // Would compute gradients for parameterized gates
283            Ok(vec![0.0; self.parameters.len()])
284        } else {
285            // No parameters, no gradients
286            Ok(vec![])
287        }
288    }
289
290    fn name(&self) -> &str {
291        &self.name
292    }
293}
294
295/// A composite layer combining rotations and entanglement
296#[derive(Debug, Clone)]
297pub struct StronglyEntanglingLayer {
298    /// Number of qubits
299    num_qubits: usize,
300    /// Rotation layers (one for each axis)
301    rotation_layers: Vec<RotationLayer>,
302    /// Entangling layer
303    entangling_layer: EntanglingLayer,
304    /// Total parameters
305    total_parameters: usize,
306    /// Layer name
307    name: String,
308}
309
310impl StronglyEntanglingLayer {
311    /// Create a new strongly entangling layer
312    pub fn new(num_qubits: usize, pattern: EntanglementPattern) -> QuantRS2Result<Self> {
313        let rotation_layers = vec![
314            RotationLayer::uniform(num_qubits, 'X')?,
315            RotationLayer::uniform(num_qubits, 'Y')?,
316            RotationLayer::uniform(num_qubits, 'Z')?,
317        ];
318
319        let entangling_layer = EntanglingLayer::new(num_qubits, pattern);
320
321        let total_parameters = rotation_layers
322            .iter()
323            .map(|layer| layer.parameters().len())
324            .sum::<usize>()
325            + entangling_layer.parameters().len();
326
327        let name = format!("StronglyEntanglingLayer_{:?}", pattern);
328
329        Ok(Self {
330            num_qubits,
331            rotation_layers,
332            entangling_layer,
333            total_parameters,
334            name,
335        })
336    }
337}
338
339impl QMLLayer for StronglyEntanglingLayer {
340    fn num_qubits(&self) -> usize {
341        self.num_qubits
342    }
343
344    fn parameters(&self) -> &[Parameter] {
345        // This is a simplified implementation
346        // In practice, we'd need to return a combined view
347        &[]
348    }
349
350    fn parameters_mut(&mut self) -> &mut [Parameter] {
351        // This is a simplified implementation
352        &mut []
353    }
354
355    fn set_parameters(&mut self, values: &[f64]) -> QuantRS2Result<()> {
356        if values.len() != self.total_parameters {
357            return Err(QuantRS2Error::InvalidInput(format!(
358                "Expected {} parameters, got {}",
359                self.total_parameters,
360                values.len()
361            )));
362        }
363
364        let mut offset = 0;
365        for layer in &mut self.rotation_layers {
366            let n = layer.parameters().len();
367            layer.set_parameters(&values[offset..offset + n])?;
368            offset += n;
369        }
370
371        if self.entangling_layer.parameterized {
372            self.entangling_layer.set_parameters(&values[offset..])?;
373        }
374
375        Ok(())
376    }
377
378    fn gates(&self) -> Vec<Box<dyn GateOp>> {
379        let mut gates = Vec::new();
380
381        // Apply rotation gates
382        for layer in &self.rotation_layers {
383            gates.extend(layer.gates());
384        }
385
386        // Apply entangling gates
387        gates.extend(self.entangling_layer.gates());
388
389        gates
390    }
391
392    fn compute_gradients(
393        &self,
394        state: &Array1<Complex64>,
395        loss_gradient: &Array1<Complex64>,
396    ) -> QuantRS2Result<Vec<f64>> {
397        let mut gradients = Vec::new();
398
399        for layer in &self.rotation_layers {
400            gradients.extend(layer.compute_gradients(state, loss_gradient)?);
401        }
402
403        if self.entangling_layer.parameterized {
404            gradients.extend(
405                self.entangling_layer
406                    .compute_gradients(state, loss_gradient)?,
407            );
408        }
409
410        Ok(gradients)
411    }
412
413    fn name(&self) -> &str {
414        &self.name
415    }
416}
417
418/// Hardware-efficient ansatz layer
419#[derive(Debug, Clone)]
420pub struct HardwareEfficientLayer {
421    /// Number of qubits
422    num_qubits: usize,
423    /// Single-qubit rotations
424    single_qubit_gates: Vec<RotationLayer>,
425    /// Two-qubit gates
426    entangling_gates: EntanglingLayer,
427    /// Layer name
428    name: String,
429}
430
431impl HardwareEfficientLayer {
432    /// Create a new hardware-efficient layer
433    pub fn new(num_qubits: usize) -> QuantRS2Result<Self> {
434        // Use RY and RZ rotations (common on hardware)
435        let single_qubit_gates = vec![
436            RotationLayer::uniform(num_qubits, 'Y')?,
437            RotationLayer::uniform(num_qubits, 'Z')?,
438        ];
439
440        // Use linear entanglement (nearest-neighbor)
441        let entangling_gates = EntanglingLayer::new(num_qubits, EntanglementPattern::Linear);
442
443        Ok(Self {
444            num_qubits,
445            single_qubit_gates,
446            entangling_gates,
447            name: "HardwareEfficientLayer".to_string(),
448        })
449    }
450}
451
452impl QMLLayer for HardwareEfficientLayer {
453    fn num_qubits(&self) -> usize {
454        self.num_qubits
455    }
456
457    fn parameters(&self) -> &[Parameter] {
458        // Simplified - would need proper implementation
459        &[]
460    }
461
462    fn parameters_mut(&mut self) -> &mut [Parameter] {
463        &mut []
464    }
465
466    fn gates(&self) -> Vec<Box<dyn GateOp>> {
467        let mut gates = Vec::new();
468
469        for layer in &self.single_qubit_gates {
470            gates.extend(layer.gates());
471        }
472
473        gates.extend(self.entangling_gates.gates());
474
475        gates
476    }
477
478    fn compute_gradients(
479        &self,
480        state: &Array1<Complex64>,
481        loss_gradient: &Array1<Complex64>,
482    ) -> QuantRS2Result<Vec<f64>> {
483        let mut gradients = Vec::new();
484
485        for layer in &self.single_qubit_gates {
486            gradients.extend(layer.compute_gradients(state, loss_gradient)?);
487        }
488
489        Ok(gradients)
490    }
491
492    fn name(&self) -> &str {
493        &self.name
494    }
495}
496
497/// Pooling layer for quantum convolutional neural networks
498#[derive(Debug, Clone)]
499pub struct QuantumPoolingLayer {
500    /// Number of input qubits
501    input_qubits: usize,
502    /// Number of output qubits (after pooling)
503    output_qubits: usize,
504    /// Pooling strategy
505    strategy: PoolingStrategy,
506    /// Layer name
507    name: String,
508}
509
510#[derive(Debug, Clone, Copy)]
511pub enum PoolingStrategy {
512    /// Trace out every other qubit
513    TraceOut,
514    /// Measure and condition
515    MeasureCondition,
516    /// Parameterized pooling
517    Parameterized,
518}
519
520impl QuantumPoolingLayer {
521    /// Create a new pooling layer
522    pub fn new(input_qubits: usize, strategy: PoolingStrategy) -> Self {
523        let output_qubits = input_qubits / 2;
524
525        Self {
526            input_qubits,
527            output_qubits,
528            strategy,
529            name: format!("QuantumPoolingLayer_{:?}", strategy),
530        }
531    }
532}
533
534impl QMLLayer for QuantumPoolingLayer {
535    fn num_qubits(&self) -> usize {
536        self.input_qubits
537    }
538
539    fn parameters(&self) -> &[Parameter] {
540        &[]
541    }
542
543    fn parameters_mut(&mut self) -> &mut [Parameter] {
544        &mut []
545    }
546
547    fn gates(&self) -> Vec<Box<dyn GateOp>> {
548        // Pooling typically involves measurements or partial traces
549        // For now, return empty
550        vec![]
551    }
552
553    fn compute_gradients(
554        &self,
555        _state: &Array1<Complex64>,
556        _loss_gradient: &Array1<Complex64>,
557    ) -> QuantRS2Result<Vec<f64>> {
558        Ok(vec![])
559    }
560
561    fn name(&self) -> &str {
562        &self.name
563    }
564}
565
566#[cfg(test)]
567mod tests {
568    use super::*;
569
570    #[test]
571    fn test_rotation_layer() {
572        let layer = RotationLayer::uniform(3, 'X').unwrap();
573        assert_eq!(layer.num_qubits(), 3);
574        assert_eq!(layer.parameters().len(), 3);
575
576        let gates = layer.gates();
577        assert_eq!(gates.len(), 3);
578    }
579
580    #[test]
581    fn test_entangling_layer() {
582        let layer = EntanglingLayer::new(4, EntanglementPattern::Linear);
583        assert_eq!(layer.num_qubits(), 4);
584
585        let gates = layer.gates();
586        assert_eq!(gates.len(), 3); // 3 CNOT gates for linear pattern
587    }
588
589    #[test]
590    fn test_strongly_entangling_layer() {
591        let layer = StronglyEntanglingLayer::new(2, EntanglementPattern::Full).unwrap();
592        assert_eq!(layer.num_qubits(), 2);
593
594        let gates = layer.gates();
595        assert_eq!(gates.len(), 7); // 6 rotation gates + 1 CNOT
596    }
597
598    #[test]
599    fn test_hardware_efficient_layer() {
600        let layer = HardwareEfficientLayer::new(3).unwrap();
601        assert_eq!(layer.num_qubits(), 3);
602
603        let gates = layer.gates();
604        assert_eq!(gates.len(), 8); // 6 rotation gates + 2 CNOTs
605    }
606}