quantrs2_core/qml/
mod.rs

1//! Quantum Machine Learning (QML) primitives and layers
2//!
3//! This module provides building blocks for quantum machine learning,
4//! including parameterized quantum circuits, data encoding strategies,
5//! and common QML layer patterns.
6
7pub mod advanced_algorithms;
8pub mod encoding;
9pub mod generative_adversarial;
10pub mod layers;
11pub mod nlp;
12pub mod reinforcement_learning;
13pub mod training;
14
15// New cutting-edge quantum ML modules
16pub mod quantum_contrastive;
17pub mod quantum_memory_networks;
18pub mod quantum_meta_learning;
19pub mod quantum_reservoir;
20pub mod quantum_transformer;
21
22// Advanced quantum ML: Privacy, Security, and Distributed Learning
23pub mod quantum_boltzmann;
24pub mod quantum_federated;
25
26// Re-export advanced QML algorithms
27pub use advanced_algorithms::{
28    FeatureMapType, QMLMetrics, QuantumEnsemble, QuantumKernel, QuantumKernelConfig, QuantumSVM,
29    QuantumTransferLearning, TransferLearningConfig, VotingStrategy,
30};
31
32// Re-export new modules
33pub use quantum_contrastive::{
34    QuantumAugmentation, QuantumContrastiveConfig, QuantumContrastiveLearner,
35};
36pub use quantum_memory_networks::{MemoryInitStrategy, QuantumMemoryConfig, QuantumMemoryNetwork};
37pub use quantum_meta_learning::{
38    QuantumMAML, QuantumMetaLearningConfig, QuantumReptile, QuantumTask,
39};
40pub use quantum_reservoir::{QuantumReservoirComputer, QuantumReservoirConfig};
41pub use quantum_transformer::{QuantumAttention, QuantumTransformer, QuantumTransformerConfig};
42
43// Re-export advanced quantum ML modules
44pub use quantum_boltzmann::{DeepQuantumBoltzmannMachine, QRBMConfig, QuantumRBM};
45pub use quantum_federated::{AggregationStrategy, QuantumFederatedConfig, QuantumFederatedServer};
46
47use crate::{
48    error::{QuantRS2Error, QuantRS2Result},
49    gate::GateOp,
50    qubit::QubitId,
51};
52use scirs2_core::ndarray::{Array1, Array2};
53use scirs2_core::Complex64;
54
55// Re-export Parameter from layers module
56pub use layers::Parameter;
57
58/// Trait for quantum machine learning layers
59pub trait QMLLayer: Send + Sync {
60    /// Get the number of qubits this layer acts on
61    fn num_qubits(&self) -> usize;
62
63    /// Get the parameters of this layer
64    fn parameters(&self) -> &[Parameter];
65
66    /// Get mutable access to parameters
67    fn parameters_mut(&mut self) -> &mut [Parameter];
68
69    /// Set parameter values
70    fn set_parameters(&mut self, values: &[f64]) -> QuantRS2Result<()> {
71        if values.len() != self.parameters().len() {
72            return Err(QuantRS2Error::InvalidInput(format!(
73                "Expected {} parameters, got {}",
74                self.parameters().len(),
75                values.len()
76            )));
77        }
78
79        for (param, &value) in self.parameters_mut().iter_mut().zip(values.iter()) {
80            param.value = value;
81        }
82
83        Ok(())
84    }
85
86    /// Get the gates that make up this layer
87    fn gates(&self) -> Vec<Box<dyn GateOp>>;
88
89    /// Compute gradients with respect to parameters
90    fn compute_gradients(
91        &self,
92        state: &Array1<Complex64>,
93        loss_gradient: &Array1<Complex64>,
94    ) -> QuantRS2Result<Vec<f64>>;
95
96    /// Get layer name
97    fn name(&self) -> &str;
98}
99
100/// Data encoding strategies for QML
101#[derive(Debug, Clone, Copy, PartialEq)]
102pub enum EncodingStrategy {
103    /// Amplitude encoding: data encoded in state amplitudes
104    Amplitude,
105    /// Angle encoding: data encoded as rotation angles
106    Angle,
107    /// IQP encoding: data encoded in diagonal gates
108    IQP,
109    /// Basis encoding: data encoded in computational basis
110    Basis,
111}
112
113/// Configuration for QML circuits
114#[derive(Debug, Clone)]
115pub struct QMLConfig {
116    /// Number of qubits
117    pub num_qubits: usize,
118    /// Number of layers
119    pub num_layers: usize,
120    /// Data encoding strategy
121    pub encoding: EncodingStrategy,
122    /// Entanglement pattern
123    pub entanglement: EntanglementPattern,
124    /// Whether to reupload data in each layer
125    pub data_reuploading: bool,
126}
127
128impl Default for QMLConfig {
129    fn default() -> Self {
130        Self {
131            num_qubits: 4,
132            num_layers: 2,
133            encoding: EncodingStrategy::Angle,
134            entanglement: EntanglementPattern::Full,
135            data_reuploading: false,
136        }
137    }
138}
139
140/// Entanglement patterns for QML layers
141#[derive(Debug, Clone, Copy, PartialEq)]
142pub enum EntanglementPattern {
143    /// No entanglement
144    None,
145    /// Linear nearest-neighbor entanglement
146    Linear,
147    /// Circular nearest-neighbor entanglement
148    Circular,
149    /// All-to-all entanglement
150    Full,
151    /// Alternating pairs
152    Alternating,
153}
154
155/// A parameterized quantum circuit for QML
156pub struct QMLCircuit {
157    /// Configuration
158    config: QMLConfig,
159    /// The layers in the circuit
160    layers: Vec<Box<dyn QMLLayer>>,
161    /// Parameter count
162    num_parameters: usize,
163}
164
165impl QMLCircuit {
166    /// Create a new QML circuit
167    pub fn new(config: QMLConfig) -> Self {
168        Self {
169            config,
170            layers: Vec::new(),
171            num_parameters: 0,
172        }
173    }
174
175    /// Add a layer to the circuit
176    pub fn add_layer(&mut self, layer: Box<dyn QMLLayer>) -> QuantRS2Result<()> {
177        if layer.num_qubits() != self.config.num_qubits {
178            return Err(QuantRS2Error::InvalidInput(format!(
179                "Layer has {} qubits, circuit has {}",
180                layer.num_qubits(),
181                self.config.num_qubits
182            )));
183        }
184
185        self.num_parameters += layer.parameters().len();
186        self.layers.push(layer);
187        Ok(())
188    }
189
190    /// Get all parameters in the circuit
191    pub fn parameters(&self) -> Vec<&Parameter> {
192        self.layers
193            .iter()
194            .flat_map(|layer| layer.parameters().iter())
195            .collect()
196    }
197
198    /// Set all parameters in the circuit
199    pub fn set_parameters(&mut self, values: &[f64]) -> QuantRS2Result<()> {
200        if values.len() != self.num_parameters {
201            return Err(QuantRS2Error::InvalidInput(format!(
202                "Expected {} parameters, got {}",
203                self.num_parameters,
204                values.len()
205            )));
206        }
207
208        let mut offset = 0;
209        for layer in &mut self.layers {
210            let layer_params = layer.parameters().len();
211            layer.set_parameters(&values[offset..offset + layer_params])?;
212            offset += layer_params;
213        }
214
215        Ok(())
216    }
217
218    /// Get all gates in the circuit
219    pub fn gates(&self) -> Vec<Box<dyn GateOp>> {
220        self.layers.iter().flat_map(|layer| layer.gates()).collect()
221    }
222
223    /// Compute gradients for all parameters
224    pub fn compute_gradients(
225        &self,
226        state: &Array1<Complex64>,
227        loss_gradient: &Array1<Complex64>,
228    ) -> QuantRS2Result<Vec<f64>> {
229        let mut all_gradients = Vec::new();
230
231        for layer in &self.layers {
232            let layer_grads = layer.compute_gradients(state, loss_gradient)?;
233            all_gradients.extend(layer_grads);
234        }
235
236        Ok(all_gradients)
237    }
238}
239
240/// Helper function to create entangling gates based on pattern
241pub fn create_entangling_gates(
242    num_qubits: usize,
243    pattern: EntanglementPattern,
244) -> Vec<(QubitId, QubitId)> {
245    match pattern {
246        EntanglementPattern::None => vec![],
247
248        EntanglementPattern::Linear => (0..num_qubits - 1)
249            .map(|i| (QubitId(i as u32), QubitId((i + 1) as u32)))
250            .collect(),
251
252        EntanglementPattern::Circular => {
253            let mut gates = vec![];
254            for i in 0..num_qubits {
255                gates.push((QubitId(i as u32), QubitId(((i + 1) % num_qubits) as u32)));
256            }
257            gates
258        }
259
260        EntanglementPattern::Full => {
261            let mut gates = vec![];
262            for i in 0..num_qubits {
263                for j in i + 1..num_qubits {
264                    gates.push((QubitId(i as u32), QubitId(j as u32)));
265                }
266            }
267            gates
268        }
269
270        EntanglementPattern::Alternating => {
271            let mut gates = vec![];
272            // Even pairs
273            for i in (0..num_qubits - 1).step_by(2) {
274                gates.push((QubitId(i as u32), QubitId((i + 1) as u32)));
275            }
276            // Odd pairs
277            for i in (1..num_qubits - 1).step_by(2) {
278                gates.push((QubitId(i as u32), QubitId((i + 1) as u32)));
279            }
280            gates
281        }
282    }
283}
284
285/// Compute the quantum Fisher information matrix
286pub fn quantum_fisher_information(
287    circuit: &QMLCircuit,
288    _state: &Array1<Complex64>,
289) -> QuantRS2Result<Array2<f64>> {
290    let num_params = circuit.num_parameters;
291    let fisher = Array2::zeros((num_params, num_params));
292
293    // Compute Fisher information using parameter shift rule
294    // F_ij = 4 * Re(<∂_i ψ | ∂_j ψ> - <∂_i ψ | ψ><ψ | ∂_j ψ>)
295
296    // This is a placeholder - full implementation would compute
297    // derivatives of the state with respect to all parameters
298
299    Ok(fisher)
300}
301
302/// Natural gradient for quantum optimization
303pub fn natural_gradient(
304    gradients: &[f64],
305    fisher: &Array2<f64>,
306    regularization: f64,
307) -> QuantRS2Result<Vec<f64>> {
308    // Add regularization to diagonal
309    let mut regularized_fisher = fisher.clone();
310    for i in 0..fisher.nrows() {
311        regularized_fisher[(i, i)] += regularization;
312    }
313
314    // Solve F * natural_grad = grad using LU decomposition
315    // This is a placeholder - would use SciRS2's linear solver when available
316    let _grad_array = Array1::from_vec(gradients.to_vec());
317
318    // For now, return the regular gradient
319    // In practice, would solve: regularized_fisher * natural_grad = grad
320    Ok(gradients.to_vec())
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326
327    #[test]
328    fn test_entanglement_patterns() {
329        let linear = create_entangling_gates(4, EntanglementPattern::Linear);
330        assert_eq!(linear.len(), 3);
331        assert_eq!(linear[0], (QubitId(0), QubitId(1)));
332
333        let circular = create_entangling_gates(4, EntanglementPattern::Circular);
334        assert_eq!(circular.len(), 4);
335        assert_eq!(circular[3], (QubitId(3), QubitId(0)));
336
337        let full = create_entangling_gates(3, EntanglementPattern::Full);
338        assert_eq!(full.len(), 3); // 3 choose 2
339
340        let none = create_entangling_gates(4, EntanglementPattern::None);
341        assert_eq!(none.len(), 0);
342    }
343
344    #[test]
345    fn test_qml_circuit() {
346        let config = QMLConfig {
347            num_qubits: 2,
348            num_layers: 1,
349            ..Default::default()
350        };
351
352        let circuit = QMLCircuit::new(config);
353        assert_eq!(circuit.num_parameters, 0);
354    }
355}