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