Skip to main content

quantrs2_device/quantum_ml/
quantum_neural_networks.rs

1//! Quantum Neural Networks
2//!
3//! This module implements various quantum neural network architectures including
4//! parameterized quantum circuits, quantum convolutional networks, and hybrid models.
5
6use super::*;
7use crate::{CircuitResult, DeviceError, DeviceResult, QuantumDevice};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::sync::Arc;
11use tokio::sync::RwLock;
12
13/// Quantum Neural Network trait
14pub trait QuantumNeuralNetwork: Send + Sync {
15    /// Forward pass through the network
16    fn forward(&self, input: &[f64]) -> DeviceResult<Vec<f64>>;
17
18    /// Get trainable parameters
19    fn parameters(&self) -> &[f64];
20
21    /// Set trainable parameters
22    fn set_parameters(&mut self, params: Vec<f64>) -> DeviceResult<()>;
23
24    /// Get parameter count
25    fn parameter_count(&self) -> usize;
26
27    /// Get network architecture description
28    fn architecture(&self) -> QNNArchitecture;
29}
30
31/// QNN Architecture description
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct QNNArchitecture {
34    pub network_type: QNNType,
35    pub num_qubits: usize,
36    pub num_layers: usize,
37    pub num_parameters: usize,
38    pub input_encoding: InputEncoding,
39    pub output_decoding: OutputDecoding,
40    pub entangling_strategy: EntanglingStrategy,
41}
42
43/// Types of quantum neural networks
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45pub enum QNNType {
46    /// Parameterized Quantum Circuit
47    PQC,
48    /// Quantum Convolutional Neural Network
49    QCNN,
50    /// Variational Quantum Classifier
51    VQC,
52    /// Quantum Generative Adversarial Network
53    QGAN,
54    /// Hybrid Classical-Quantum Network
55    HybridCQN,
56    /// Quantum Recurrent Neural Network
57    QRNN,
58}
59
60/// Input encoding strategies
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub enum InputEncoding {
63    /// Amplitude encoding
64    Amplitude,
65    /// Angle encoding
66    Angle,
67    /// Basis encoding
68    Basis,
69    /// Coherent state encoding (for CV systems)
70    CoherentState,
71    /// Displacement encoding
72    Displacement,
73}
74
75/// Output decoding strategies
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
77pub enum OutputDecoding {
78    /// Expectation value of Pauli operators
79    PauliExpectation,
80    /// Measurement probabilities
81    Probabilities,
82    /// Fidelity measurement
83    Fidelity,
84    /// Coherent state measurement
85    CoherentMeasurement,
86}
87
88/// Entangling strategies
89#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
90pub enum EntanglingStrategy {
91    Linear,
92    Circular,
93    AllToAll,
94    Random,
95    Hardware,
96    Custom(Vec<(usize, usize)>),
97}
98
99/// Parameterized Quantum Circuit Network
100pub struct PQCNetwork {
101    device: Arc<RwLock<dyn QuantumDevice + Send + Sync>>,
102    num_qubits: usize,
103    num_layers: usize,
104    parameters: Vec<f64>,
105    input_encoding: InputEncoding,
106    output_decoding: OutputDecoding,
107    entangling_strategy: EntanglingStrategy,
108    measurement_operators: Vec<PauliOperator>,
109}
110
111impl PQCNetwork {
112    /// Create a new PQC network
113    pub fn new(
114        device: Arc<RwLock<dyn QuantumDevice + Send + Sync>>,
115        num_qubits: usize,
116        num_layers: usize,
117        input_encoding: InputEncoding,
118        output_decoding: OutputDecoding,
119        entangling_strategy: EntanglingStrategy,
120    ) -> Self {
121        let parameter_count = Self::calculate_parameter_count(num_qubits, num_layers);
122        let parameters = (0..parameter_count)
123            .map(|_| fastrand::f64() * 2.0 * std::f64::consts::PI)
124            .collect();
125
126        let measurement_operators = (0..num_qubits).map(|_| PauliOperator::Z).collect();
127
128        Self {
129            device,
130            num_qubits,
131            num_layers,
132            parameters,
133            input_encoding,
134            output_decoding,
135            entangling_strategy,
136            measurement_operators,
137        }
138    }
139
140    const fn calculate_parameter_count(num_qubits: usize, num_layers: usize) -> usize {
141        // Each layer has 3 rotation gates per qubit
142        3 * num_qubits * num_layers
143    }
144
145    /// Build the quantum circuit for given input
146    pub async fn build_circuit(&self, input: &[f64]) -> DeviceResult<ParameterizedQuantumCircuit> {
147        let mut circuit = ParameterizedQuantumCircuit::new(self.num_qubits);
148
149        // Input encoding
150        self.encode_input(&mut circuit, input).await?;
151
152        // Parameterized layers
153        let mut param_idx = 0;
154        for layer in 0..self.num_layers {
155            // Rotation gates
156            for qubit in 0..self.num_qubits {
157                circuit.add_rx_gate(qubit, self.parameters[param_idx])?;
158                param_idx += 1;
159                circuit.add_ry_gate(qubit, self.parameters[param_idx])?;
160                param_idx += 1;
161                circuit.add_rz_gate(qubit, self.parameters[param_idx])?;
162                param_idx += 1;
163            }
164
165            // Entangling gates
166            self.add_entangling_gates(&mut circuit, layer).await?;
167        }
168
169        Ok(circuit)
170    }
171
172    async fn encode_input(
173        &self,
174        circuit: &mut ParameterizedQuantumCircuit,
175        input: &[f64],
176    ) -> DeviceResult<()> {
177        match self.input_encoding {
178            InputEncoding::Angle => {
179                // Encode input as rotation angles
180                let padded_input = self.pad_input(input, self.num_qubits);
181                for (qubit, &value) in padded_input.iter().enumerate() {
182                    circuit.add_ry_gate(qubit, value)?;
183                }
184            }
185            InputEncoding::Amplitude => {
186                // Amplitude encoding requires state preparation
187                // This is a simplified implementation
188                for qubit in 0..self.num_qubits {
189                    circuit.add_h_gate(qubit)?;
190                }
191                // Would need more sophisticated amplitude encoding
192            }
193            InputEncoding::Basis => {
194                // Basis encoding: encode classical bits as computational basis states
195                let binary_input = self.convert_to_binary(input);
196                for (qubit, &bit) in binary_input.iter().enumerate() {
197                    if bit == 1 {
198                        circuit.add_x_gate(qubit)?;
199                    }
200                }
201            }
202            _ => {
203                return Err(DeviceError::InvalidInput(format!(
204                    "Input encoding {:?} not implemented for PQC",
205                    self.input_encoding
206                )));
207            }
208        }
209        Ok(())
210    }
211
212    async fn add_entangling_gates(
213        &self,
214        circuit: &mut ParameterizedQuantumCircuit,
215        _layer: usize,
216    ) -> DeviceResult<()> {
217        match &self.entangling_strategy {
218            EntanglingStrategy::Linear => {
219                for qubit in 0..self.num_qubits - 1 {
220                    circuit.add_cnot_gate(qubit, qubit + 1)?;
221                }
222            }
223            EntanglingStrategy::Circular => {
224                for qubit in 0..self.num_qubits - 1 {
225                    circuit.add_cnot_gate(qubit, qubit + 1)?;
226                }
227                if self.num_qubits > 2 {
228                    circuit.add_cnot_gate(self.num_qubits - 1, 0)?;
229                }
230            }
231            EntanglingStrategy::AllToAll => {
232                for i in 0..self.num_qubits {
233                    for j in i + 1..self.num_qubits {
234                        circuit.add_cnot_gate(i, j)?;
235                    }
236                }
237            }
238            EntanglingStrategy::Custom(connections) => {
239                for &(control, target) in connections {
240                    if control < self.num_qubits && target < self.num_qubits {
241                        circuit.add_cnot_gate(control, target)?;
242                    }
243                }
244            }
245            _ => {
246                // Default to linear
247                for qubit in 0..self.num_qubits - 1 {
248                    circuit.add_cnot_gate(qubit, qubit + 1)?;
249                }
250            }
251        }
252        Ok(())
253    }
254
255    fn pad_input(&self, input: &[f64], target_size: usize) -> Vec<f64> {
256        let mut padded = input.to_vec();
257        while padded.len() < target_size {
258            padded.push(0.0);
259        }
260        padded.truncate(target_size);
261        padded
262    }
263
264    fn convert_to_binary(&self, input: &[f64]) -> Vec<u8> {
265        let mut binary = Vec::new();
266        for &value in input {
267            let int_value = (value * 255.0) as u8;
268            for i in 0..8 {
269                binary.push((int_value >> i) & 1);
270                if binary.len() >= self.num_qubits {
271                    break;
272                }
273            }
274            if binary.len() >= self.num_qubits {
275                break;
276            }
277        }
278        while binary.len() < self.num_qubits {
279            binary.push(0);
280        }
281        binary.truncate(self.num_qubits);
282        binary
283    }
284
285    async fn decode_output(&self, circuit_result: &CircuitResult) -> DeviceResult<Vec<f64>> {
286        match self.output_decoding {
287            OutputDecoding::PauliExpectation => {
288                // Compute expectation values of Pauli operators
289                let mut expectations = Vec::new();
290                for (qubit, pauli_op) in self.measurement_operators.iter().enumerate() {
291                    let expectation =
292                        self.compute_pauli_expectation(circuit_result, qubit, pauli_op)?;
293                    expectations.push(expectation);
294                }
295                Ok(expectations)
296            }
297            OutputDecoding::Probabilities => {
298                // Convert measurement counts to probabilities
299                let total_shots = circuit_result.shots as f64;
300                let mut probs = Vec::new();
301
302                for i in 0..self.num_qubits {
303                    let mut prob_one = 0.0;
304                    for (bitstring, count) in &circuit_result.counts {
305                        if let Some(bit_char) = bitstring.chars().nth(i) {
306                            if bit_char == '1' {
307                                prob_one += *count as f64 / total_shots;
308                            }
309                        }
310                    }
311                    probs.push(prob_one);
312                }
313                Ok(probs)
314            }
315            _ => Err(DeviceError::InvalidInput(format!(
316                "Output decoding {:?} not implemented",
317                self.output_decoding
318            ))),
319        }
320    }
321
322    fn compute_pauli_expectation(
323        &self,
324        circuit_result: &CircuitResult,
325        qubit: usize,
326        pauli_op: &PauliOperator,
327    ) -> DeviceResult<f64> {
328        let mut expectation = 0.0;
329        let total_shots = circuit_result.shots as f64;
330
331        for (bitstring, count) in &circuit_result.counts {
332            let probability = *count as f64 / total_shots;
333
334            let eigenvalue = if let Some(bit_char) = bitstring.chars().nth(qubit) {
335                match pauli_op {
336                    PauliOperator::Z => {
337                        if bit_char == '0' {
338                            1.0
339                        } else {
340                            -1.0
341                        }
342                    }
343                    PauliOperator::X | PauliOperator::Y => {
344                        // Would need different measurement basis
345                        return Err(DeviceError::InvalidInput(
346                            "X and Y Pauli measurements require basis rotation".to_string(),
347                        ));
348                    }
349                    PauliOperator::I => 1.0,
350                }
351            } else {
352                0.0
353            };
354
355            expectation += probability * eigenvalue;
356        }
357
358        Ok(expectation)
359    }
360
361    /// Execute a circuit on the quantum device (helper function to work around trait object limitations)
362    async fn execute_circuit_helper(
363        _device: &(dyn QuantumDevice + Send + Sync),
364        circuit: &ParameterizedQuantumCircuit,
365        shots: usize,
366    ) -> DeviceResult<CircuitResult> {
367        // Evaluate the circuit with the in-crate exact state-vector simulator
368        // and sample real measurement counts (previously a fabricated uniform
369        // 50/50 split that silently corrupted every QNN forward pass / loss).
370        crate::quantum_ml::circuit_simulation::simulate_and_sample(circuit, shots)
371    }
372}
373
374impl QuantumNeuralNetwork for PQCNetwork {
375    fn forward(&self, input: &[f64]) -> DeviceResult<Vec<f64>> {
376        // This would need to be async in practice
377        let rt = tokio::runtime::Runtime::new().map_err(|e| {
378            DeviceError::ExecutionFailed(format!("Failed to create tokio runtime: {e}"))
379        })?;
380        rt.block_on(async {
381            let circuit = self.build_circuit(input).await?;
382            let device = self.device.read().await;
383            let result = Self::execute_circuit_helper(&*device, &circuit, 1024).await?;
384            self.decode_output(&result).await
385        })
386    }
387
388    fn parameters(&self) -> &[f64] {
389        &self.parameters
390    }
391
392    fn set_parameters(&mut self, params: Vec<f64>) -> DeviceResult<()> {
393        if params.len() != self.parameters.len() {
394            return Err(DeviceError::InvalidInput(format!(
395                "Expected {} parameters, got {}",
396                self.parameters.len(),
397                params.len()
398            )));
399        }
400        self.parameters = params;
401        Ok(())
402    }
403
404    fn parameter_count(&self) -> usize {
405        self.parameters.len()
406    }
407
408    fn architecture(&self) -> QNNArchitecture {
409        QNNArchitecture {
410            network_type: QNNType::PQC,
411            num_qubits: self.num_qubits,
412            num_layers: self.num_layers,
413            num_parameters: self.parameters.len(),
414            input_encoding: self.input_encoding.clone(),
415            output_decoding: self.output_decoding.clone(),
416            entangling_strategy: self.entangling_strategy.clone(),
417        }
418    }
419}
420
421/// Quantum Convolutional Neural Network
422pub struct QCNN {
423    device: Arc<RwLock<dyn QuantumDevice + Send + Sync>>,
424    num_qubits: usize,
425    conv_layers: Vec<QConvLayer>,
426    pooling_layers: Vec<QPoolingLayer>,
427    parameters: Vec<f64>,
428    input_encoding: InputEncoding,
429}
430
431/// Quantum Convolutional Layer
432#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct QConvLayer {
434    pub kernel_size: usize,
435    pub stride: usize,
436    pub num_filters: usize,
437    pub parameter_indices: Vec<usize>,
438}
439
440/// Quantum Pooling Layer
441#[derive(Debug, Clone, Serialize, Deserialize)]
442pub struct QPoolingLayer {
443    pub pool_size: usize,
444    pub pool_type: QPoolingType,
445}
446
447#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
448pub enum QPoolingType {
449    Max,
450    Average,
451    Measurement,
452}
453
454impl QCNN {
455    pub fn new(
456        device: Arc<RwLock<dyn QuantumDevice + Send + Sync>>,
457        num_qubits: usize,
458        conv_layers: Vec<QConvLayer>,
459        pooling_layers: Vec<QPoolingLayer>,
460        input_encoding: InputEncoding,
461    ) -> Self {
462        let total_params = conv_layers.iter()
463            .map(|layer| layer.num_filters * layer.kernel_size * 3) // 3 rotation gates per kernel
464            .sum();
465
466        let parameters = (0..total_params)
467            .map(|_| fastrand::f64() * 2.0 * std::f64::consts::PI)
468            .collect();
469
470        Self {
471            device,
472            num_qubits,
473            conv_layers,
474            pooling_layers,
475            parameters,
476            input_encoding,
477        }
478    }
479
480    pub async fn build_circuit(&self, input: &[f64]) -> DeviceResult<ParameterizedQuantumCircuit> {
481        let mut circuit = ParameterizedQuantumCircuit::new(self.num_qubits);
482
483        // Input encoding
484        self.encode_input(&mut circuit, input).await?;
485
486        let mut current_qubits = self.num_qubits;
487
488        // Apply convolutional and pooling layers alternately
489        for (conv_layer, pool_layer) in self.conv_layers.iter().zip(self.pooling_layers.iter()) {
490            // Apply convolutional layer
491            self.apply_conv_layer(&mut circuit, conv_layer, current_qubits)
492                .await?;
493
494            // Apply pooling layer
495            current_qubits = self
496                .apply_pooling_layer(&mut circuit, pool_layer, current_qubits)
497                .await?;
498        }
499
500        Ok(circuit)
501    }
502
503    async fn encode_input(
504        &self,
505        circuit: &mut ParameterizedQuantumCircuit,
506        input: &[f64],
507    ) -> DeviceResult<()> {
508        match self.input_encoding {
509            InputEncoding::Angle => {
510                let padded_input = self.pad_input(input, self.num_qubits);
511                for (qubit, &value) in padded_input.iter().enumerate() {
512                    circuit.add_ry_gate(qubit, value)?;
513                }
514            }
515            InputEncoding::Amplitude => {
516                // Initialize in superposition
517                for qubit in 0..self.num_qubits {
518                    circuit.add_h_gate(qubit)?;
519                }
520            }
521            _ => {
522                return Err(DeviceError::InvalidInput(format!(
523                    "Input encoding {:?} not implemented for QCNN",
524                    self.input_encoding
525                )));
526            }
527        }
528        Ok(())
529    }
530
531    async fn apply_conv_layer(
532        &self,
533        circuit: &mut ParameterizedQuantumCircuit,
534        layer: &QConvLayer,
535        num_active_qubits: usize,
536    ) -> DeviceResult<()> {
537        let num_windows = (num_active_qubits - layer.kernel_size) / layer.stride + 1;
538
539        for window in 0..num_windows {
540            let start_qubit = window * layer.stride;
541
542            for filter in 0..layer.num_filters {
543                let param_offset = filter * layer.kernel_size * 3;
544
545                // Apply parameterized gates to qubits in the window
546                for i in 0..layer.kernel_size {
547                    let qubit = start_qubit + i;
548                    let param_base = param_offset + i * 3;
549
550                    if param_base + 2 < self.parameters.len() {
551                        circuit.add_rx_gate(qubit, self.parameters[param_base])?;
552                        circuit.add_ry_gate(qubit, self.parameters[param_base + 1])?;
553                        circuit.add_rz_gate(qubit, self.parameters[param_base + 2])?;
554                    }
555                }
556
557                // Apply entangling gates within the window
558                for i in 0..layer.kernel_size - 1 {
559                    let control = start_qubit + i;
560                    let target = start_qubit + i + 1;
561                    circuit.add_cnot_gate(control, target)?;
562                }
563            }
564        }
565
566        Ok(())
567    }
568
569    async fn apply_pooling_layer(
570        &self,
571        circuit: &mut ParameterizedQuantumCircuit,
572        layer: &QPoolingLayer,
573        num_active_qubits: usize,
574    ) -> DeviceResult<usize> {
575        let num_pools = num_active_qubits / layer.pool_size;
576
577        match layer.pool_type {
578            QPoolingType::Measurement => {
579                // Measure and discard some qubits (simplified)
580                // In practice, this would involve partial measurements
581                Ok(num_pools)
582            }
583            QPoolingType::Max | QPoolingType::Average => {
584                // Apply pooling unitaries (simplified)
585                for pool in 0..num_pools {
586                    let start_qubit = pool * layer.pool_size;
587
588                    // Apply pooling gates
589                    for i in 0..layer.pool_size - 1 {
590                        let qubit1 = start_qubit + i;
591                        let qubit2 = start_qubit + i + 1;
592                        circuit.add_cnot_gate(qubit1, qubit2)?;
593                    }
594                }
595                Ok(num_pools)
596            }
597        }
598    }
599
600    fn pad_input(&self, input: &[f64], target_size: usize) -> Vec<f64> {
601        let mut padded = input.to_vec();
602        while padded.len() < target_size {
603            padded.push(0.0);
604        }
605        padded.truncate(target_size);
606        padded
607    }
608}
609
610impl QuantumNeuralNetwork for QCNN {
611    fn forward(&self, input: &[f64]) -> DeviceResult<Vec<f64>> {
612        let rt = tokio::runtime::Runtime::new().map_err(|e| {
613            DeviceError::ExecutionFailed(format!("Failed to create tokio runtime: {e}"))
614        })?;
615        rt.block_on(async {
616            let circuit = self.build_circuit(input).await?;
617            let device = self.device.read().await;
618            let result = Self::execute_circuit_helper(&*device, &circuit, 1024).await?;
619
620            // Simple output decoding for QCNN
621            let mut output = Vec::new();
622            let total_shots = result.shots as f64;
623
624            for i in 0..self.num_qubits.min(8) {
625                // Limit output size
626                let mut prob_one = 0.0;
627                for (bitstring, count) in &result.counts {
628                    if let Some(bit_char) = bitstring.chars().nth(i) {
629                        if bit_char == '1' {
630                            prob_one += *count as f64 / total_shots;
631                        }
632                    }
633                }
634                output.push(prob_one);
635            }
636
637            Ok(output)
638        })
639    }
640
641    fn parameters(&self) -> &[f64] {
642        &self.parameters
643    }
644
645    fn set_parameters(&mut self, params: Vec<f64>) -> DeviceResult<()> {
646        if params.len() != self.parameters.len() {
647            return Err(DeviceError::InvalidInput(format!(
648                "Expected {} parameters, got {}",
649                self.parameters.len(),
650                params.len()
651            )));
652        }
653        self.parameters = params;
654        Ok(())
655    }
656
657    fn parameter_count(&self) -> usize {
658        self.parameters.len()
659    }
660
661    fn architecture(&self) -> QNNArchitecture {
662        QNNArchitecture {
663            network_type: QNNType::QCNN,
664            num_qubits: self.num_qubits,
665            num_layers: self.conv_layers.len(),
666            num_parameters: self.parameters.len(),
667            input_encoding: self.input_encoding.clone(),
668            output_decoding: OutputDecoding::Probabilities,
669            entangling_strategy: EntanglingStrategy::Linear,
670        }
671    }
672}
673
674impl QCNN {
675    /// Execute a circuit on the quantum device (helper function to work around trait object limitations)
676    async fn execute_circuit_helper(
677        _device: &(dyn QuantumDevice + Send + Sync),
678        circuit: &ParameterizedQuantumCircuit,
679        shots: usize,
680    ) -> DeviceResult<CircuitResult> {
681        // Evaluate the circuit with the in-crate exact state-vector simulator
682        // and sample real measurement counts (previously a fabricated uniform
683        // 50/50 split that silently corrupted every QNN forward pass / loss).
684        crate::quantum_ml::circuit_simulation::simulate_and_sample(circuit, shots)
685    }
686}
687
688/// Variational Quantum Classifier
689pub struct VQC {
690    pqc_network: PQCNetwork,
691    class_mapping: HashMap<usize, String>,
692}
693
694impl VQC {
695    pub fn new(
696        device: Arc<RwLock<dyn QuantumDevice + Send + Sync>>,
697        num_qubits: usize,
698        num_layers: usize,
699        num_classes: usize,
700    ) -> Self {
701        let pqc_network = PQCNetwork::new(
702            device,
703            num_qubits,
704            num_layers,
705            InputEncoding::Angle,
706            OutputDecoding::PauliExpectation,
707            EntanglingStrategy::Linear,
708        );
709
710        let class_mapping = (0..num_classes)
711            .map(|i| (i, format!("class_{i}")))
712            .collect();
713
714        Self {
715            pqc_network,
716            class_mapping,
717        }
718    }
719
720    pub fn classify(&self, input: &[f64]) -> DeviceResult<ClassificationResult> {
721        let raw_output = self.pqc_network.forward(input)?;
722
723        // Convert quantum outputs to class probabilities
724        let class_probs = self.softmax(&raw_output);
725
726        // Find predicted class
727        let (predicted_class, confidence) = class_probs
728            .iter()
729            .enumerate()
730            .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
731            .map_or((0, 0.0), |(idx, &prob)| (idx, prob));
732
733        let class_name = self
734            .class_mapping
735            .get(&predicted_class)
736            .cloned()
737            .unwrap_or_else(|| "unknown".to_string());
738
739        Ok(ClassificationResult {
740            predicted_class,
741            class_name,
742            confidence,
743            class_probabilities: class_probs,
744        })
745    }
746
747    fn softmax(&self, values: &[f64]) -> Vec<f64> {
748        let max_val = values.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
749        let exp_values: Vec<f64> = values.iter().map(|&x| (x - max_val).exp()).collect();
750        let sum_exp: f64 = exp_values.iter().sum();
751        exp_values.iter().map(|&x| x / sum_exp).collect()
752    }
753}
754
755impl QuantumNeuralNetwork for VQC {
756    fn forward(&self, input: &[f64]) -> DeviceResult<Vec<f64>> {
757        self.pqc_network.forward(input)
758    }
759
760    fn parameters(&self) -> &[f64] {
761        self.pqc_network.parameters()
762    }
763
764    fn set_parameters(&mut self, params: Vec<f64>) -> DeviceResult<()> {
765        self.pqc_network.set_parameters(params)
766    }
767
768    fn parameter_count(&self) -> usize {
769        self.pqc_network.parameter_count()
770    }
771
772    fn architecture(&self) -> QNNArchitecture {
773        let mut arch = self.pqc_network.architecture();
774        arch.network_type = QNNType::VQC;
775        arch
776    }
777}
778
779/// Classification result
780#[derive(Debug, Clone, Serialize, Deserialize)]
781pub struct ClassificationResult {
782    pub predicted_class: usize,
783    pub class_name: String,
784    pub confidence: f64,
785    pub class_probabilities: Vec<f64>,
786}
787
788/// Create a PQC network for classification
789pub fn create_pqc_classifier(
790    device: Arc<RwLock<dyn QuantumDevice + Send + Sync>>,
791    num_features: usize,
792    num_classes: usize,
793    num_layers: usize,
794) -> DeviceResult<VQC> {
795    let num_qubits =
796        (num_features as f64).log2().ceil() as usize + (num_classes as f64).log2().ceil() as usize;
797    Ok(VQC::new(device, num_qubits, num_layers, num_classes))
798}
799
800/// Create a QCNN for image classification
801pub fn create_qcnn_classifier(
802    device: Arc<RwLock<dyn QuantumDevice + Send + Sync>>,
803    image_size: usize,
804) -> DeviceResult<QCNN> {
805    let num_qubits = (image_size as f64).log2().ceil() as usize;
806
807    let conv_layers = vec![
808        QConvLayer {
809            kernel_size: 2,
810            stride: 1,
811            num_filters: 2,
812            parameter_indices: (0..12).collect(), // 2 filters * 2 kernel_size * 3 params
813        },
814        QConvLayer {
815            kernel_size: 2,
816            stride: 1,
817            num_filters: 1,
818            parameter_indices: (12..18).collect(), // 1 filter * 2 kernel_size * 3 params
819        },
820    ];
821
822    let pooling_layers = vec![
823        QPoolingLayer {
824            pool_size: 2,
825            pool_type: QPoolingType::Measurement,
826        },
827        QPoolingLayer {
828            pool_size: 2,
829            pool_type: QPoolingType::Measurement,
830        },
831    ];
832
833    Ok(QCNN::new(
834        device,
835        num_qubits,
836        conv_layers,
837        pooling_layers,
838        InputEncoding::Angle,
839    ))
840}
841
842#[cfg(test)]
843mod tests {
844    use super::*;
845    use crate::test_utils::create_mock_quantum_device;
846
847    #[test]
848    fn test_pqc_network_creation() {
849        let device = create_mock_quantum_device();
850        let network = PQCNetwork::new(
851            device,
852            4,
853            2,
854            InputEncoding::Angle,
855            OutputDecoding::PauliExpectation,
856            EntanglingStrategy::Linear,
857        );
858
859        assert_eq!(network.num_qubits, 4);
860        assert_eq!(network.num_layers, 2);
861        assert_eq!(network.parameter_count(), 24); // 3 * 4 * 2
862    }
863
864    #[test]
865    fn test_vqc_creation() {
866        let device = create_mock_quantum_device();
867        let classifier = VQC::new(device, 4, 2, 3);
868
869        assert_eq!(classifier.class_mapping.len(), 3);
870        assert_eq!(classifier.parameter_count(), 24);
871    }
872
873    #[test]
874    fn test_qcnn_creation() {
875        let device = create_mock_quantum_device();
876        let conv_layers = vec![QConvLayer {
877            kernel_size: 2,
878            stride: 1,
879            num_filters: 1,
880            parameter_indices: (0..6).collect(),
881        }];
882        let pooling_layers = vec![QPoolingLayer {
883            pool_size: 2,
884            pool_type: QPoolingType::Max,
885        }];
886
887        let qcnn = QCNN::new(device, 4, conv_layers, pooling_layers, InputEncoding::Angle);
888
889        assert_eq!(qcnn.num_qubits, 4);
890        assert_eq!(qcnn.parameter_count(), 6);
891    }
892
893    #[test]
894    fn test_softmax() {
895        let classifier = {
896            let device = create_mock_quantum_device();
897            VQC::new(device, 4, 2, 3)
898        };
899
900        let input = vec![1.0, 2.0, 3.0];
901        let output = classifier.softmax(&input);
902
903        assert_eq!(output.len(), 3);
904        assert!((output.iter().sum::<f64>() - 1.0).abs() < 1e-10);
905        assert!(output[2] > output[1]);
906        assert!(output[1] > output[0]);
907    }
908
909    #[test]
910    fn test_parameter_operations() {
911        let device = create_mock_quantum_device();
912        let mut network = PQCNetwork::new(
913            device,
914            4,
915            2,
916            InputEncoding::Angle,
917            OutputDecoding::PauliExpectation,
918            EntanglingStrategy::Linear,
919        );
920
921        let original_params = network.parameters().to_vec();
922        let new_params = vec![0.0; network.parameter_count()];
923
924        network
925            .set_parameters(new_params.clone())
926            .expect("Setting parameters should succeed");
927        assert_eq!(network.parameters(), &new_params);
928        assert_ne!(network.parameters(), &original_params);
929
930        // Test invalid parameter count
931        let invalid_params = vec![0.0; 5];
932        assert!(network.set_parameters(invalid_params).is_err());
933    }
934}