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