Skip to main content

quantrs2_ml/computer_vision/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5// SciRS2 Policy: Unified imports
6use crate::error::{MLError, Result};
7use crate::optimization::OptimizationMethod;
8use crate::qcnn::PoolingType;
9use crate::qnn::QNNLayerType;
10use crate::qnn::QuantumNeuralNetwork;
11use crate::quantum_transformer::{
12    QuantumAttentionType, QuantumTransformer, QuantumTransformerConfig,
13};
14use quantrs2_circuit::builder::Circuit;
15use scirs2_core::ndarray::*;
16use scirs2_core::random::prelude::*;
17use scirs2_core::random::{ChaCha20Rng, Rng, SeedableRng};
18use scirs2_core::{Complex32, Complex64};
19use std::collections::HashMap;
20use std::f64::consts::PI;
21
22/// Data augmentation configuration
23pub trait VisionModel: std::fmt::Debug {
24    /// Forward pass through the model
25    fn forward(&self, input: &Array4<f64>) -> Result<Array4<f64>>;
26    /// Get model parameters
27    fn parameters(&self) -> &Array1<f64>;
28    /// Update parameters
29    fn update_parameters(&mut self, params: &Array1<f64>) -> Result<()>;
30    /// Number of parameters
31    fn num_parameters(&self) -> usize;
32    /// Clone the model
33    fn clone_box(&self) -> Box<dyn VisionModel>;
34}
35
36pub trait TaskHead: std::fmt::Debug {
37    /// Process features for specific task
38    fn forward(&self, features: &Array4<f64>) -> Result<TaskOutput>;
39    /// Get head parameters
40    fn parameters(&self) -> &Array1<f64>;
41    /// Update parameters
42    fn update_parameters(&mut self, params: &Array1<f64>) -> Result<()>;
43    /// Clone the head
44    fn clone_box(&self) -> Box<dyn TaskHead>;
45}
46
47#[derive(Debug, Clone)]
48pub struct AugmentationConfig {
49    /// Random horizontal flip
50    pub horizontal_flip: bool,
51    /// Random rotation range
52    pub rotation_range: f64,
53    /// Random zoom range
54    pub zoom_range: (f64, f64),
55    /// Random brightness adjustment
56    pub brightness_range: (f64, f64),
57    /// Quantum noise injection
58    pub quantum_noise: bool,
59}
60impl AugmentationConfig {
61    /// Default augmentation
62    pub fn default() -> Self {
63        Self {
64            horizontal_flip: true,
65            rotation_range: 15.0,
66            zoom_range: (0.8, 1.2),
67            brightness_range: (0.8, 1.2),
68            quantum_noise: false,
69        }
70    }
71    /// Detection augmentation
72    pub fn detection() -> Self {
73        Self {
74            horizontal_flip: true,
75            rotation_range: 5.0,
76            zoom_range: (0.9, 1.1),
77            brightness_range: (0.9, 1.1),
78            quantum_noise: true,
79        }
80    }
81    /// Segmentation augmentation
82    pub fn segmentation() -> Self {
83        Self {
84            horizontal_flip: true,
85            rotation_range: 10.0,
86            zoom_range: (0.85, 1.15),
87            brightness_range: (0.85, 1.15),
88            quantum_noise: false,
89        }
90    }
91}
92#[derive(Debug, Clone)]
93pub struct FeatureExtractionHead {
94    pub feature_dim: usize,
95    pub normalize: bool,
96    pub parameters: Array1<f64>,
97}
98impl FeatureExtractionHead {
99    fn new(feature_dim: usize, normalize: bool) -> Result<Self> {
100        Ok(Self {
101            feature_dim,
102            normalize,
103            parameters: Array1::zeros(50),
104        })
105    }
106}
107/// Normalization parameters
108#[derive(Debug, Clone)]
109pub struct NormalizationParams {
110    pub mean: Array1<f64>,
111    pub std: Array1<f64>,
112}
113/// Task target types
114#[derive(Debug, Clone)]
115pub enum TaskTarget {
116    Classification {
117        labels: Vec<usize>,
118    },
119    Detection {
120        boxes: Array3<f64>,
121        labels: Array2<usize>,
122    },
123    Segmentation {
124        masks: Array4<usize>,
125    },
126    Features {
127        target_features: Array2<f64>,
128    },
129}
130/// Image preprocessor
131#[derive(Debug, Clone)]
132pub struct ImagePreprocessor {
133    /// Preprocessing configuration
134    pub config: PreprocessingConfig,
135    /// Normalization parameters
136    pub norm_params: NormalizationParams,
137}
138impl ImagePreprocessor {
139    /// Create new image preprocessor
140    pub fn new(config: PreprocessingConfig) -> Self {
141        let norm_params = NormalizationParams {
142            mean: Array1::from_vec(config.mean.clone()),
143            std: Array1::from_vec(config.std.clone()),
144        };
145        Self {
146            config,
147            norm_params,
148        }
149    }
150    /// Preprocess images
151    pub fn preprocess(&self, images: &Array4<f64>) -> Result<Array4<f64>> {
152        let mut processed = images.clone();
153        if images.dim().2 != self.config.image_size.0 || images.dim().3 != self.config.image_size.1
154        {
155            processed = self.resize(&processed, self.config.image_size)?;
156        }
157        if self.config.normalize {
158            processed = self.normalize(&processed)?;
159        }
160        if self.config.augmentation.horizontal_flip && fastrand::f64() > 0.5 {
161            processed = self.horizontal_flip(&processed)?;
162        }
163        Ok(processed)
164    }
165    /// Resize images
166    fn resize(&self, images: &Array4<f64>, size: (usize, usize)) -> Result<Array4<f64>> {
167        let (batch_size, channels, _, _) = images.dim();
168        let mut resized = Array4::zeros((batch_size, channels, size.0, size.1));
169        for b in 0..batch_size {
170            for c in 0..channels {
171                for h in 0..size.0 {
172                    for w in 0..size.1 {
173                        let src_h = h * images.dim().2 / size.0;
174                        let src_w = w * images.dim().3 / size.1;
175                        resized[[b, c, h, w]] = images[[b, c, src_h, src_w]];
176                    }
177                }
178            }
179        }
180        Ok(resized)
181    }
182    /// Normalize images
183    fn normalize(&self, images: &Array4<f64>) -> Result<Array4<f64>> {
184        let mut normalized = images.clone();
185        let channels = images.dim().1;
186        for c in 0..channels.min(self.norm_params.mean.len()) {
187            let mean = self.norm_params.mean[c];
188            let std = self.norm_params.std[c];
189            normalized
190                .slice_mut(s![.., c, .., ..])
191                .mapv_inplace(|x| (x - mean) / std);
192        }
193        Ok(normalized)
194    }
195    /// Horizontal flip
196    fn horizontal_flip(&self, images: &Array4<f64>) -> Result<Array4<f64>> {
197        let (batch_size, channels, height, width) = images.dim();
198        let mut flipped = Array4::zeros((batch_size, channels, height, width));
199        for b in 0..batch_size {
200            for c in 0..channels {
201                for h in 0..height {
202                    for w in 0..width {
203                        flipped[[b, c, h, w]] = images[[b, c, h, width - 1 - w]];
204                    }
205                }
206            }
207        }
208        Ok(flipped)
209    }
210}
211/// Quantum enhancement levels
212#[derive(Debug, Clone)]
213pub enum QuantumEnhancement {
214    /// Minimal quantum processing
215    Low,
216    /// Balanced quantum-classical
217    Medium,
218    /// Maximum quantum advantage
219    High,
220    /// Custom enhancement
221    Custom {
222        quantum_layers: Vec<usize>,
223        entanglement_strength: f64,
224    },
225}
226/// Vision performance metrics
227#[derive(Debug, Clone)]
228pub struct VisionMetrics {
229    /// Task-specific metrics
230    pub task_metrics: HashMap<String, f64>,
231    /// Quantum metrics
232    pub quantum_metrics: QuantumMetrics,
233    /// Computational metrics
234    pub computational_metrics: ComputationalMetrics,
235}
236impl VisionMetrics {
237    /// Create new vision metrics
238    pub fn new() -> Self {
239        Self {
240            task_metrics: HashMap::new(),
241            quantum_metrics: QuantumMetrics {
242                circuit_depth: 0,
243                entanglement_entropy: 0.0,
244                quantum_advantage: 1.0,
245                coherence_utilization: 0.8,
246            },
247            computational_metrics: ComputationalMetrics {
248                flops: 0.0,
249                memory_mb: 0.0,
250                inference_ms: 0.0,
251                throughput: 0.0,
252            },
253        }
254    }
255}
256/// Quantum spatial attention
257#[derive(Debug, Clone)]
258pub struct QuantumSpatialAttention {
259    /// Number of attention heads
260    pub num_heads: usize,
261    /// Attention dimension
262    pub attention_dim: usize,
263    /// Quantum attention circuit parameters
264    pub attention_circuit_params: Vec<Vec<f64>>,
265}
266impl QuantumSpatialAttention {
267    /// Create new quantum spatial attention
268    pub fn new(num_heads: usize, attention_dim: usize, num_qubits: usize) -> Result<Self> {
269        let mut attention_circuit_params = Vec::new();
270        for _ in 0..num_heads {
271            let mut params = Vec::new();
272            for _ in 0..num_qubits.min(attention_dim / 8) {
273                params.push(1.0);
274                params.push(0.0);
275            }
276            attention_circuit_params.push(params);
277        }
278        Ok(Self {
279            num_heads,
280            attention_dim,
281            attention_circuit_params,
282        })
283    }
284    /// Apply spatial attention
285    pub fn apply(&self, features: &Array4<f64>) -> Result<Array4<f64>> {
286        Ok(features.clone())
287    }
288}
289/// Computational metrics
290#[derive(Debug, Clone)]
291pub struct ComputationalMetrics {
292    /// FLOPs per image
293    pub flops: f64,
294    /// Memory usage (MB)
295    pub memory_mb: f64,
296    /// Inference time (ms)
297    pub inference_ms: f64,
298    /// Throughput (images/sec)
299    pub throughput: f64,
300}
301/// Quantum convolutional neural network wrapper
302#[derive(Debug, Clone)]
303pub struct QuantumConvolutionalNN {
304    /// Number of filters
305    pub num_filters: usize,
306    /// Kernel size
307    pub kernel_size: usize,
308    /// Number of qubits
309    pub num_qubits: usize,
310    /// Parameters
311    pub parameters: Array1<f64>,
312}
313impl QuantumConvolutionalNN {
314    fn new(
315        _layers: Vec<QNNLayerType>,
316        num_qubits: usize,
317        _input_size: usize,
318        num_filters: usize,
319    ) -> Result<Self> {
320        Ok(Self {
321            num_filters,
322            kernel_size: 3,
323            num_qubits,
324            parameters: Array1::zeros(100),
325        })
326    }
327}
328/// Quantum ViT backbone
329#[derive(Debug, Clone)]
330pub struct QuantumViTBackbone {
331    /// Patch size
332    pub patch_size: usize,
333    /// Embedding dimension
334    pub embed_dim: usize,
335    /// Transformer
336    pub transformer: QuantumTransformer,
337    /// Model parameters
338    pub parameters: Array1<f64>,
339}
340impl QuantumViTBackbone {
341    fn new(
342        patch_size: usize,
343        embed_dim: usize,
344        num_heads: usize,
345        depth: usize,
346        num_qubits: usize,
347    ) -> Result<Self> {
348        let config = QuantumTransformerConfig {
349            model_dim: embed_dim,
350            num_heads,
351            ff_dim: embed_dim * 4,
352            num_layers: depth,
353            max_seq_len: 1024,
354            num_qubits,
355            dropout_rate: 0.1,
356            attention_type: QuantumAttentionType::QuantumEnhancedMultiHead,
357            position_encoding: crate::quantum_transformer::PositionEncodingType::LearnableQuantum,
358        };
359        let transformer = QuantumTransformer::new(config)?;
360        Ok(Self {
361            patch_size,
362            embed_dim,
363            transformer,
364            parameters: Array1::zeros(1000),
365        })
366    }
367}
368/// Quantum EfficientNet backbone
369#[derive(Debug, Clone)]
370pub struct QuantumEfficientNetBackbone {
371    /// Width coefficient
372    pub width_coefficient: f64,
373    /// Depth coefficient
374    pub depth_coefficient: f64,
375    /// Number of qubits
376    pub num_qubits: usize,
377    /// Model parameters
378    pub parameters: Array1<f64>,
379}
380impl QuantumEfficientNetBackbone {
381    fn new(width_coefficient: f64, depth_coefficient: f64, num_qubits: usize) -> Result<Self> {
382        Ok(Self {
383            width_coefficient,
384            depth_coefficient,
385            num_qubits,
386            parameters: Array1::zeros(800),
387        })
388    }
389}
390/// Hybrid vision backbone
391#[derive(Debug, Clone)]
392pub struct HybridVisionBackbone {
393    /// CNN layers
394    pub cnn_layers: usize,
395    /// Transformer layers
396    pub transformer_layers: usize,
397    /// Number of qubits
398    pub num_qubits: usize,
399    /// Model parameters
400    pub parameters: Array1<f64>,
401}
402impl HybridVisionBackbone {
403    fn new(cnn_layers: usize, transformer_layers: usize, num_qubits: usize) -> Result<Self> {
404        Ok(Self {
405            cnn_layers,
406            transformer_layers,
407            num_qubits,
408            parameters: Array1::zeros(500),
409        })
410    }
411}
412#[derive(Debug, Clone)]
413pub struct SegmentationHead {
414    pub num_classes: usize,
415    pub output_stride: usize,
416    pub parameters: Array1<f64>,
417}
418impl SegmentationHead {
419    fn new(num_classes: usize, output_stride: usize) -> Result<Self> {
420        Ok(Self {
421            num_classes,
422            output_stride,
423            parameters: Array1::zeros(200),
424        })
425    }
426}
427/// Image encoding methods for quantum circuits
428#[derive(Debug, Clone)]
429pub enum ImageEncodingMethod {
430    /// Amplitude encoding (efficient for grayscale)
431    AmplitudeEncoding,
432    /// Angle encoding (preserves spatial information)
433    AngleEncoding { basis: String },
434    /// FRQI (Flexible Representation of Quantum Images)
435    FRQI,
436    /// NEQR (Novel Enhanced Quantum Representation)
437    NEQR { gray_levels: usize },
438    /// QPIE (Quantum Probability Image Encoding)
439    QPIE,
440    /// Hierarchical encoding for multi-scale
441    HierarchicalEncoding { levels: usize },
442}
443/// Color space options
444#[derive(Debug, Clone)]
445pub enum ColorSpace {
446    RGB,
447    Grayscale,
448    HSV,
449    LAB,
450    YCbCr,
451}
452#[derive(Debug, Clone)]
453pub struct InstanceSegmentationHead {
454    pub num_classes: usize,
455    pub mask_resolution: (usize, usize),
456    pub parameters: Array1<f64>,
457}
458impl InstanceSegmentationHead {
459    fn new(num_classes: usize, mask_resolution: (usize, usize)) -> Result<Self> {
460        Ok(Self {
461            num_classes,
462            mask_resolution,
463            parameters: Array1::zeros(300),
464        })
465    }
466}
467/// Quantum image encoder
468#[derive(Debug, Clone)]
469pub struct QuantumImageEncoder {
470    /// Encoding method
471    pub method: ImageEncodingMethod,
472    /// Number of qubits
473    pub num_qubits: usize,
474    /// Encoding circuits
475    pub encoding_circuits: Vec<Circuit<16>>,
476    /// Encoding parameters
477    pub parameters: Array1<f64>,
478}
479impl QuantumImageEncoder {
480    /// Create new quantum image encoder
481    pub fn new(method: ImageEncodingMethod, num_qubits: usize) -> Result<Self> {
482        let encoding_circuits = match &method {
483            ImageEncodingMethod::AmplitudeEncoding => {
484                Self::create_amplitude_encoding_circuits(num_qubits)?
485            }
486            ImageEncodingMethod::AngleEncoding { basis } => {
487                Self::create_angle_encoding_circuits(num_qubits, basis)?
488            }
489            ImageEncodingMethod::FRQI => Self::create_frqi_circuits(num_qubits)?,
490            ImageEncodingMethod::NEQR { gray_levels } => {
491                Self::create_neqr_circuits(num_qubits, *gray_levels)?
492            }
493            ImageEncodingMethod::QPIE => Self::create_qpie_circuits(num_qubits)?,
494            ImageEncodingMethod::HierarchicalEncoding { levels } => {
495                Self::create_hierarchical_circuits(num_qubits, *levels)?
496            }
497        };
498        let parameters = Array1::zeros(encoding_circuits.len() * 10);
499        Ok(Self {
500            method,
501            num_qubits,
502            encoding_circuits,
503            parameters,
504        })
505    }
506    /// Encode images to quantum states
507    pub fn encode(&self, images: &Array4<f64>) -> Result<Array4<f64>> {
508        let (batch_size, channels, height, width) = images.dim();
509        let mut encoded = Array4::zeros((batch_size, channels, height, width));
510        for b in 0..batch_size {
511            for c in 0..channels {
512                let image_slice = images.slice(s![b, c, .., ..]).to_owned();
513                let encoded_slice = self.encode_single_channel(&image_slice)?;
514                encoded.slice_mut(s![b, c, .., ..]).assign(&encoded_slice);
515            }
516        }
517        Ok(encoded)
518    }
519    /// Encode single channel
520    fn encode_single_channel(&self, channel: &Array2<f64>) -> Result<Array2<f64>> {
521        Ok(channel.mapv(|x| (x * PI).sin()))
522    }
523    /// Create amplitude encoding circuits
524    fn create_amplitude_encoding_circuits(num_qubits: usize) -> Result<Vec<Circuit<16>>> {
525        let mut circuits = Vec::new();
526        let mut circuit = Circuit::<16>::new();
527        for i in 0..num_qubits.min(16) {
528            circuit.h(i);
529        }
530        for i in 0..num_qubits.min(16) {
531            circuit.ry(i, 0.0);
532        }
533        circuits.push(circuit);
534        Ok(circuits)
535    }
536    /// Create angle encoding circuits
537    fn create_angle_encoding_circuits(num_qubits: usize, basis: &str) -> Result<Vec<Circuit<16>>> {
538        let mut circuits = Vec::new();
539        let mut circuit = Circuit::<16>::new();
540        match basis {
541            "x" => {
542                for i in 0..num_qubits.min(16) {
543                    circuit.rx(i, 0.0);
544                }
545            }
546            "y" => {
547                for i in 0..num_qubits.min(16) {
548                    circuit.ry(i, 0.0);
549                }
550            }
551            "z" => {
552                for i in 0..num_qubits.min(16) {
553                    circuit.rz(i, 0.0);
554                }
555            }
556            _ => {
557                for i in 0..num_qubits.min(16) {
558                    circuit.ry(i, 0.0);
559                }
560            }
561        }
562        circuits.push(circuit);
563        Ok(circuits)
564    }
565    /// Create FRQI circuits
566    fn create_frqi_circuits(num_qubits: usize) -> Result<Vec<Circuit<16>>> {
567        let mut circuits = Vec::new();
568        let mut circuit = Circuit::<16>::new();
569        let position_qubits = (num_qubits - 1).min(15);
570        for i in 0..position_qubits {
571            circuit.h(i);
572        }
573        if position_qubits < 16 {
574            circuit.ry(position_qubits, 0.0);
575        }
576        circuits.push(circuit);
577        Ok(circuits)
578    }
579    /// Create NEQR circuits
580    fn create_neqr_circuits(num_qubits: usize, gray_levels: usize) -> Result<Vec<Circuit<16>>> {
581        let mut circuits = Vec::new();
582        let gray_qubits = (gray_levels as f64).log2().ceil() as usize;
583        let position_qubits = num_qubits - gray_qubits;
584        let mut circuit = Circuit::<16>::new();
585        for i in 0..position_qubits.min(16) {
586            circuit.h(i);
587        }
588        for i in position_qubits..num_qubits.min(16) {
589            circuit.ry(i, 0.0);
590        }
591        circuits.push(circuit);
592        Ok(circuits)
593    }
594    /// Create QPIE circuits
595    fn create_qpie_circuits(num_qubits: usize) -> Result<Vec<Circuit<16>>> {
596        let mut circuits = Vec::new();
597        let mut circuit = Circuit::<16>::new();
598        for i in 0..num_qubits.min(16) {
599            circuit.h(i);
600            circuit.ry(i, 0.0);
601        }
602        for i in 0..(num_qubits - 1).min(15) {
603            circuit.cnot(i, i + 1);
604        }
605        circuits.push(circuit);
606        Ok(circuits)
607    }
608    /// Create hierarchical encoding circuits
609    fn create_hierarchical_circuits(num_qubits: usize, levels: usize) -> Result<Vec<Circuit<16>>> {
610        let mut circuits = Vec::new();
611        let qubits_per_level = num_qubits / levels;
612        for level in 0..levels {
613            let mut circuit = Circuit::<16>::new();
614            let start_qubit = level * qubits_per_level;
615            let end_qubit = ((level + 1) * qubits_per_level).min(num_qubits).min(16);
616            for i in start_qubit..end_qubit {
617                circuit.h(i);
618                circuit.ry(i, 0.0);
619            }
620            if level > 0 && start_qubit > 0 && start_qubit < 16 {
621                circuit.cnot(start_qubit - 1, start_qubit);
622            }
623            circuits.push(circuit);
624        }
625        Ok(circuits)
626    }
627}
628/// Quantum computer vision pipeline configuration
629#[derive(Debug, Clone)]
630pub struct QuantumVisionConfig {
631    /// Number of qubits for encoding
632    pub num_qubits: usize,
633    /// Image encoding method
634    pub encoding_method: ImageEncodingMethod,
635    /// Vision backbone type
636    pub backbone: VisionBackbone,
637    /// Task-specific configuration
638    pub task_config: VisionTaskConfig,
639    /// Preprocessing configuration
640    pub preprocessing: PreprocessingConfig,
641    /// Quantum enhancement level
642    pub quantum_enhancement: QuantumEnhancement,
643}
644impl QuantumVisionConfig {
645    /// Create default configuration
646    pub fn default() -> Self {
647        Self {
648            num_qubits: 12,
649            encoding_method: ImageEncodingMethod::AmplitudeEncoding,
650            backbone: VisionBackbone::QuantumCNN {
651                conv_layers: vec![
652                    ConvolutionalConfig {
653                        num_filters: 32,
654                        kernel_size: 3,
655                        stride: 1,
656                        padding: 1,
657                        quantum_kernel: true,
658                        circuit_depth: 4,
659                    },
660                    ConvolutionalConfig {
661                        num_filters: 64,
662                        kernel_size: 3,
663                        stride: 2,
664                        padding: 1,
665                        quantum_kernel: true,
666                        circuit_depth: 6,
667                    },
668                ],
669                pooling_type: PoolingType::Quantum,
670            },
671            task_config: VisionTaskConfig::Classification {
672                num_classes: 10,
673                multi_label: false,
674            },
675            preprocessing: PreprocessingConfig::default(),
676            quantum_enhancement: QuantumEnhancement::Medium,
677        }
678    }
679    /// Create configuration for object detection
680    pub fn object_detection(num_classes: usize) -> Self {
681        Self {
682            num_qubits: 16,
683            encoding_method: ImageEncodingMethod::NEQR { gray_levels: 256 },
684            backbone: VisionBackbone::HybridBackbone {
685                cnn_layers: 4,
686                transformer_layers: 2,
687            },
688            task_config: VisionTaskConfig::ObjectDetection {
689                num_classes,
690                anchor_sizes: vec![(32, 32), (64, 64), (128, 128)],
691                iou_threshold: 0.5,
692            },
693            preprocessing: PreprocessingConfig::detection_default(),
694            quantum_enhancement: QuantumEnhancement::High,
695        }
696    }
697    /// Create configuration for segmentation
698    pub fn segmentation(num_classes: usize) -> Self {
699        Self {
700            num_qubits: 14,
701            encoding_method: ImageEncodingMethod::HierarchicalEncoding { levels: 3 },
702            backbone: VisionBackbone::QuantumViT {
703                patch_size: 16,
704                embed_dim: 768,
705                num_heads: 12,
706                depth: 12,
707            },
708            task_config: VisionTaskConfig::Segmentation {
709                num_classes,
710                output_stride: 8,
711            },
712            preprocessing: PreprocessingConfig::segmentation_default(),
713            quantum_enhancement: QuantumEnhancement::High,
714        }
715    }
716}
717/// Preprocessing configuration
718#[derive(Debug, Clone)]
719pub struct PreprocessingConfig {
720    /// Target image size
721    pub image_size: (usize, usize),
722    /// Normalization parameters
723    pub normalize: bool,
724    pub mean: Vec<f64>,
725    pub std: Vec<f64>,
726    /// Data augmentation
727    pub augmentation: AugmentationConfig,
728    /// Color space
729    pub color_space: ColorSpace,
730}
731impl PreprocessingConfig {
732    /// Default preprocessing
733    pub fn default() -> Self {
734        Self {
735            image_size: (224, 224),
736            normalize: true,
737            mean: vec![0.485, 0.456, 0.406],
738            std: vec![0.229, 0.224, 0.225],
739            augmentation: AugmentationConfig::default(),
740            color_space: ColorSpace::RGB,
741        }
742    }
743    /// Detection preprocessing
744    pub fn detection_default() -> Self {
745        Self {
746            image_size: (416, 416),
747            normalize: true,
748            mean: vec![0.5, 0.5, 0.5],
749            std: vec![0.5, 0.5, 0.5],
750            augmentation: AugmentationConfig::detection(),
751            color_space: ColorSpace::RGB,
752        }
753    }
754    /// Segmentation preprocessing
755    pub fn segmentation_default() -> Self {
756        Self {
757            image_size: (512, 512),
758            normalize: true,
759            mean: vec![0.485, 0.456, 0.406],
760            std: vec![0.229, 0.224, 0.225],
761            augmentation: AugmentationConfig::segmentation(),
762            color_space: ColorSpace::RGB,
763        }
764    }
765}
766/// Vision backbone architectures
767#[derive(Debug, Clone)]
768pub enum VisionBackbone {
769    /// Quantum Convolutional Neural Network
770    QuantumCNN {
771        conv_layers: Vec<ConvolutionalConfig>,
772        pooling_type: PoolingType,
773    },
774    /// Vision Transformer with quantum attention
775    QuantumViT {
776        patch_size: usize,
777        embed_dim: usize,
778        num_heads: usize,
779        depth: usize,
780    },
781    /// Hybrid CNN-Transformer
782    HybridBackbone {
783        cnn_layers: usize,
784        transformer_layers: usize,
785    },
786    /// Quantum ResNet
787    QuantumResNet {
788        blocks: Vec<ResidualBlock>,
789        skip_connections: bool,
790    },
791    /// Quantum EfficientNet
792    QuantumEfficientNet {
793        width_coefficient: f64,
794        depth_coefficient: f64,
795    },
796}
797/// Main quantum computer vision pipeline
798#[derive(Debug, Clone)]
799pub struct QuantumVisionPipeline {
800    /// Pipeline configuration
801    pub config: QuantumVisionConfig,
802    /// Image encoder
803    pub encoder: QuantumImageEncoder,
804    /// Vision backbone
805    pub backbone: Box<dyn VisionModel>,
806    /// Task-specific head
807    pub task_head: Box<dyn TaskHead>,
808    /// Feature extractor
809    pub feature_extractor: QuantumFeatureExtractor,
810    /// Preprocessing pipeline
811    pub preprocessor: ImagePreprocessor,
812    /// Performance metrics
813    pub metrics: VisionMetrics,
814    /// Backbone parameter gradient estimated by the most recent `backward`
815    /// call, consumed (and cleared) by the next `update_parameters` call.
816    backbone_gradient: Option<Array1<f64>>,
817    /// Task head parameter gradient estimated by the most recent `backward`
818    /// call, consumed (and cleared) by the next `update_parameters` call.
819    task_head_gradient: Option<Array1<f64>>,
820}
821impl QuantumVisionPipeline {
822    /// Create new quantum vision pipeline
823    pub fn new(config: QuantumVisionConfig) -> Result<Self> {
824        let encoder = QuantumImageEncoder::new(config.encoding_method.clone(), config.num_qubits)?;
825        let backbone: Box<dyn VisionModel> = match &config.backbone {
826            VisionBackbone::QuantumCNN {
827                conv_layers,
828                pooling_type,
829            } => Box::new(QuantumCNNBackbone::new(
830                conv_layers.clone(),
831                pooling_type.clone(),
832                config.num_qubits,
833            )?),
834            VisionBackbone::QuantumViT {
835                patch_size,
836                embed_dim,
837                num_heads,
838                depth,
839            } => Box::new(QuantumViTBackbone::new(
840                *patch_size,
841                *embed_dim,
842                *num_heads,
843                *depth,
844                config.num_qubits,
845            )?),
846            VisionBackbone::HybridBackbone {
847                cnn_layers,
848                transformer_layers,
849            } => Box::new(HybridVisionBackbone::new(
850                *cnn_layers,
851                *transformer_layers,
852                config.num_qubits,
853            )?),
854            VisionBackbone::QuantumResNet {
855                blocks,
856                skip_connections,
857            } => Box::new(QuantumResNetBackbone::new(
858                blocks.clone(),
859                *skip_connections,
860                config.num_qubits,
861            )?),
862            VisionBackbone::QuantumEfficientNet {
863                width_coefficient,
864                depth_coefficient,
865            } => Box::new(QuantumEfficientNetBackbone::new(
866                *width_coefficient,
867                *depth_coefficient,
868                config.num_qubits,
869            )?),
870        };
871        let task_head: Box<dyn TaskHead> = match &config.task_config {
872            VisionTaskConfig::Classification {
873                num_classes,
874                multi_label,
875            } => Box::new(ClassificationHead::new(*num_classes, *multi_label)?),
876            VisionTaskConfig::ObjectDetection {
877                num_classes,
878                anchor_sizes,
879                iou_threshold,
880            } => Box::new(DetectionHead::new(
881                *num_classes,
882                anchor_sizes.clone(),
883                *iou_threshold,
884            )?),
885            VisionTaskConfig::Segmentation {
886                num_classes,
887                output_stride,
888            } => Box::new(SegmentationHead::new(*num_classes, *output_stride)?),
889            VisionTaskConfig::InstanceSegmentation {
890                num_classes,
891                mask_resolution,
892            } => Box::new(InstanceSegmentationHead::new(
893                *num_classes,
894                *mask_resolution,
895            )?),
896            VisionTaskConfig::FeatureExtraction {
897                feature_dim,
898                normalize,
899            } => Box::new(FeatureExtractionHead::new(*feature_dim, *normalize)?),
900            VisionTaskConfig::Generation {
901                latent_dim,
902                output_channels,
903            } => Box::new(GenerationHead::new(*latent_dim, *output_channels)?),
904        };
905        let feature_extractor = QuantumFeatureExtractor::new(512, config.num_qubits)?;
906        let preprocessor = ImagePreprocessor::new(config.preprocessing.clone());
907        let metrics = VisionMetrics::new();
908        Ok(Self {
909            config,
910            encoder,
911            backbone,
912            task_head,
913            feature_extractor,
914            preprocessor,
915            metrics,
916            backbone_gradient: None,
917            task_head_gradient: None,
918        })
919    }
920    /// Process images through the pipeline
921    pub fn forward(&mut self, images: &Array4<f64>) -> Result<TaskOutput> {
922        let (batch_size, channels, height, width) = images.dim();
923        let processed = self.preprocessor.preprocess(images)?;
924        let encoded = self.encoder.encode(&processed)?;
925        let features = self.backbone.forward(&encoded)?;
926        let quantum_features = self.feature_extractor.extract(&features)?;
927        let output = self.task_head.forward(&quantum_features)?;
928        self.update_metrics(&features, &output);
929        Ok(output)
930    }
931    /// Train the pipeline
932    pub fn train(
933        &mut self,
934        train_data: &[(Array4<f64>, TaskTarget)],
935        val_data: &[(Array4<f64>, TaskTarget)],
936        epochs: usize,
937        optimizer: OptimizationMethod,
938    ) -> Result<TrainingHistory> {
939        let mut history = TrainingHistory::new();
940        for epoch in 0..epochs {
941            let mut train_loss = 0.0;
942            for (images, target) in train_data {
943                let output = self.forward(images)?;
944                let loss = Self::compute_loss(&output, target)?;
945                self.backward(images, target, loss)?;
946                self.update_parameters(&optimizer)?;
947                train_loss += loss;
948            }
949            let mut val_loss = 0.0;
950            let mut val_metrics = HashMap::new();
951            for (images, target) in val_data {
952                let output = self.forward(images)?;
953                let loss = Self::compute_loss(&output, target)?;
954                val_loss += loss;
955                let metrics = self.evaluate_metrics(&output, target)?;
956                for (key, value) in metrics {
957                    *val_metrics.entry(key).or_insert(0.0) += value;
958                }
959            }
960            train_loss /= train_data.len() as f64;
961            val_loss /= val_data.len() as f64;
962            for value in val_metrics.values_mut() {
963                *value /= val_data.len() as f64;
964            }
965            history.add_epoch(epoch, train_loss, val_loss, val_metrics);
966            println!(
967                "Epoch {}/{}: train_loss={:.4}, val_loss={:.4}",
968                epoch + 1,
969                epochs,
970                train_loss,
971                val_loss
972            );
973        }
974        Ok(history)
975    }
976    /// Compute loss for the task
977    fn compute_loss(output: &TaskOutput, target: &TaskTarget) -> Result<f64> {
978        match (output, target) {
979            (TaskOutput::Classification { logits, .. }, TaskTarget::Classification { labels }) => {
980                let mut loss = 0.0;
981                for (logit_row, &label) in logits.outer_iter().zip(labels.iter()) {
982                    let max_logit = logit_row.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
983                    let exp_logits: Vec<f64> =
984                        logit_row.iter().map(|&x| (x - max_logit).exp()).collect();
985                    let sum_exp: f64 = exp_logits.iter().sum();
986                    let prob = exp_logits[label] / sum_exp;
987                    loss -= prob.ln();
988                }
989                Ok(loss / labels.len() as f64)
990            }
991            _ => Ok(0.1),
992        }
993    }
994    /// Re-run the pipeline's forward pass (bypassing metrics bookkeeping)
995    /// and compute the resulting loss for `images`/`target`. Used by
996    /// `backward` to numerically probe the loss surface at perturbed
997    /// parameter values.
998    fn forward_loss(&self, images: &Array4<f64>, target: &TaskTarget) -> Result<f64> {
999        let processed = self.preprocessor.preprocess(images)?;
1000        let encoded = self.encoder.encode(&processed)?;
1001        let features = self.backbone.forward(&encoded)?;
1002        let quantum_features = self.feature_extractor.extract(&features)?;
1003        let output = self.task_head.forward(&quantum_features)?;
1004        Self::compute_loss(&output, target)
1005    }
1006
1007    /// Estimate a real SPSA (simultaneous perturbation stochastic
1008    /// approximation) gradient of the pipeline's loss with respect to the
1009    /// backbone's flat parameter vector: both the "+" and "-" perturbed
1010    /// losses are obtained by actually re-running the pipeline's forward
1011    /// pass and loss computation (not fabricated), and the backbone's
1012    /// parameters are restored to their original value before returning.
1013    fn estimate_backbone_gradient(
1014        &mut self,
1015        images: &Array4<f64>,
1016        target: &TaskTarget,
1017    ) -> Result<Array1<f64>> {
1018        const PERTURBATION_SCALE: f64 = 1e-2;
1019
1020        let base_params = self.backbone.parameters().clone();
1021        let mut rng = thread_rng();
1022        let direction: Array1<f64> =
1023            base_params.mapv(|_| if rng.random::<f64>() < 0.5 { -1.0 } else { 1.0 });
1024
1025        let plus_params = &base_params + &(&direction * PERTURBATION_SCALE);
1026        self.backbone.update_parameters(&plus_params)?;
1027        let loss_plus = self.forward_loss(images, target)?;
1028
1029        let minus_params = &base_params - &(&direction * PERTURBATION_SCALE);
1030        self.backbone.update_parameters(&minus_params)?;
1031        let loss_minus = self.forward_loss(images, target)?;
1032
1033        // Restore the original parameters.
1034        self.backbone.update_parameters(&base_params)?;
1035
1036        let loss_delta = loss_plus - loss_minus;
1037        Ok(direction.mapv(|d| (loss_delta / (2.0 * PERTURBATION_SCALE)) * d))
1038    }
1039
1040    /// Same as [`Self::estimate_backbone_gradient`], but for the task head's
1041    /// flat parameter vector.
1042    fn estimate_task_head_gradient(
1043        &mut self,
1044        images: &Array4<f64>,
1045        target: &TaskTarget,
1046    ) -> Result<Array1<f64>> {
1047        const PERTURBATION_SCALE: f64 = 1e-2;
1048
1049        let base_params = self.task_head.parameters().clone();
1050        let mut rng = thread_rng();
1051        let direction: Array1<f64> =
1052            base_params.mapv(|_| if rng.random::<f64>() < 0.5 { -1.0 } else { 1.0 });
1053
1054        let plus_params = &base_params + &(&direction * PERTURBATION_SCALE);
1055        self.task_head.update_parameters(&plus_params)?;
1056        let loss_plus = self.forward_loss(images, target)?;
1057
1058        let minus_params = &base_params - &(&direction * PERTURBATION_SCALE);
1059        self.task_head.update_parameters(&minus_params)?;
1060        let loss_minus = self.forward_loss(images, target)?;
1061
1062        // Restore the original parameters.
1063        self.task_head.update_parameters(&base_params)?;
1064
1065        let loss_delta = loss_plus - loss_minus;
1066        Ok(direction.mapv(|d| (loss_delta / (2.0 * PERTURBATION_SCALE)) * d))
1067    }
1068
1069    /// Backward pass: estimates a real gradient of the training loss with
1070    /// respect to both the backbone's and the task head's flat parameter
1071    /// vectors via SPSA, and stashes the resulting gradients for the next
1072    /// [`Self::update_parameters`] call to apply. Previously this was a
1073    /// complete no-op, so `train`'s per-epoch loss reporting was honest but
1074    /// no parameter ever changed.
1075    fn backward(&mut self, images: &Array4<f64>, target: &TaskTarget, _loss: f64) -> Result<()> {
1076        self.backbone_gradient = if self.backbone.parameters().is_empty() {
1077            None
1078        } else {
1079            Some(self.estimate_backbone_gradient(images, target)?)
1080        };
1081
1082        self.task_head_gradient = if self.task_head.parameters().is_empty() {
1083            None
1084        } else {
1085            Some(self.estimate_task_head_gradient(images, target)?)
1086        };
1087
1088        Ok(())
1089    }
1090
1091    /// Apply the gradients estimated by the most recent [`Self::backward`]
1092    /// call to the backbone's and task head's parameters via a real
1093    /// gradient-descent step (previously this method did not touch any
1094    /// parameters at all).
1095    fn update_parameters(&mut self, optimizer: &OptimizationMethod) -> Result<()> {
1096        // `OptimizationMethod` is a bare method tag (it carries no learning
1097        // rate of its own), so every variant currently takes a real gradient
1098        // step at the same fixed learning rate; distinguishing per-optimizer
1099        // dynamics (e.g. Adam moment estimates) is not implemented yet.
1100        let learning_rate = 0.01;
1101        let _ = optimizer;
1102
1103        if let Some(gradient) = self.backbone_gradient.take() {
1104            let updated = self.backbone.parameters() - &(&gradient * learning_rate);
1105            self.backbone.update_parameters(&updated)?;
1106        }
1107        if let Some(gradient) = self.task_head_gradient.take() {
1108            let updated = self.task_head.parameters() - &(&gradient * learning_rate);
1109            self.task_head.update_parameters(&updated)?;
1110        }
1111        Ok(())
1112    }
1113    /// Evaluate metrics
1114    fn evaluate_metrics(
1115        &self,
1116        output: &TaskOutput,
1117        target: &TaskTarget,
1118    ) -> Result<HashMap<String, f64>> {
1119        let mut metrics = HashMap::new();
1120        match (output, target) {
1121            (
1122                TaskOutput::Classification { probabilities, .. },
1123                TaskTarget::Classification { labels },
1124            ) => {
1125                let mut correct = 0;
1126                for (prob_row, &label) in probabilities.outer_iter().zip(labels.iter()) {
1127                    let predicted = prob_row
1128                        .iter()
1129                        .enumerate()
1130                        .max_by(|(_, a), (_, b)| {
1131                            a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
1132                        })
1133                        .map(|(i, _)| i)
1134                        .unwrap_or(0);
1135                    if predicted == label {
1136                        correct += 1;
1137                    }
1138                }
1139                metrics.insert("accuracy".to_string(), correct as f64 / labels.len() as f64);
1140            }
1141            _ => {}
1142        }
1143        Ok(metrics)
1144    }
1145    /// Update performance metrics
1146    fn update_metrics(&mut self, features: &Array4<f64>, output: &TaskOutput) {
1147        self.metrics.quantum_metrics.entanglement_entropy =
1148            self.compute_entanglement_entropy(features);
1149        self.metrics.computational_metrics.inference_ms = 10.0;
1150        self.metrics.computational_metrics.throughput = 100.0;
1151    }
1152    /// Compute entanglement entropy
1153    fn compute_entanglement_entropy(&self, features: &Array4<f64>) -> f64 {
1154        let variance = features.var(0.0);
1155        variance.ln()
1156    }
1157    /// Get performance metrics
1158    pub fn metrics(&self) -> &VisionMetrics {
1159        &self.metrics
1160    }
1161}
1162/// Other task heads (placeholder implementations)
1163#[derive(Debug, Clone)]
1164pub struct DetectionHead {
1165    pub num_classes: usize,
1166    pub anchor_sizes: Vec<(usize, usize)>,
1167    pub iou_threshold: f64,
1168    pub parameters: Array1<f64>,
1169}
1170impl DetectionHead {
1171    fn new(
1172        num_classes: usize,
1173        anchor_sizes: Vec<(usize, usize)>,
1174        iou_threshold: f64,
1175    ) -> Result<Self> {
1176        Ok(Self {
1177            num_classes,
1178            anchor_sizes,
1179            iou_threshold,
1180            parameters: Array1::zeros(100),
1181        })
1182    }
1183}
1184/// Convolutional layer configuration
1185#[derive(Debug, Clone)]
1186pub struct ConvolutionalConfig {
1187    /// Number of filters
1188    pub num_filters: usize,
1189    /// Kernel size
1190    pub kernel_size: usize,
1191    /// Stride
1192    pub stride: usize,
1193    /// Padding
1194    pub padding: usize,
1195    /// Use quantum kernel
1196    pub quantum_kernel: bool,
1197    /// Circuit depth
1198    pub circuit_depth: usize,
1199}
1200/// Task output variants
1201#[derive(Debug, Clone)]
1202pub enum TaskOutput {
1203    /// Classification logits
1204    Classification {
1205        logits: Array2<f64>,
1206        probabilities: Array2<f64>,
1207    },
1208    /// Detection outputs
1209    Detection {
1210        boxes: Array3<f64>,
1211        scores: Array2<f64>,
1212        classes: Array2<usize>,
1213    },
1214    /// Segmentation masks
1215    Segmentation {
1216        masks: Array4<f64>,
1217        class_scores: Array4<f64>,
1218    },
1219    /// Extracted features
1220    Features {
1221        features: Array2<f64>,
1222        attention_maps: Option<Array4<f64>>,
1223    },
1224    /// Generated images
1225    Generation {
1226        images: Array4<f64>,
1227        latent_codes: Array2<f64>,
1228    },
1229}
1230/// Quantum ResNet backbone
1231#[derive(Debug, Clone)]
1232pub struct QuantumResNetBackbone {
1233    /// Residual blocks
1234    pub blocks: Vec<ResidualBlock>,
1235    /// Skip connections
1236    pub skip_connections: bool,
1237    /// Number of qubits
1238    pub num_qubits: usize,
1239    /// Model parameters
1240    pub parameters: Array1<f64>,
1241}
1242impl QuantumResNetBackbone {
1243    fn new(blocks: Vec<ResidualBlock>, skip_connections: bool, num_qubits: usize) -> Result<Self> {
1244        Ok(Self {
1245            blocks,
1246            skip_connections,
1247            num_qubits,
1248            parameters: Array1::zeros(1000),
1249        })
1250    }
1251}
1252#[derive(Debug, Clone)]
1253pub struct GenerationHead {
1254    pub latent_dim: usize,
1255    pub output_channels: usize,
1256    pub parameters: Array1<f64>,
1257}
1258impl GenerationHead {
1259    fn new(latent_dim: usize, output_channels: usize) -> Result<Self> {
1260        Ok(Self {
1261            latent_dim,
1262            output_channels,
1263            parameters: Array1::zeros(400),
1264        })
1265    }
1266}
1267/// Classification head
1268#[derive(Debug, Clone)]
1269pub struct ClassificationHead {
1270    /// Number of classes
1271    pub num_classes: usize,
1272    /// Multi-label classification
1273    pub multi_label: bool,
1274    /// Quantum classifier
1275    pub classifier: QuantumNeuralNetwork,
1276}
1277impl ClassificationHead {
1278    fn new(num_classes: usize, multi_label: bool) -> Result<Self> {
1279        let layers = vec![
1280            QNNLayerType::EncodingLayer { num_features: 512 },
1281            QNNLayerType::VariationalLayer { num_params: 256 },
1282            QNNLayerType::VariationalLayer {
1283                num_params: num_classes,
1284            },
1285            QNNLayerType::MeasurementLayer {
1286                measurement_basis: "computational".to_string(),
1287            },
1288        ];
1289        let classifier = QuantumNeuralNetwork::new(layers, 10, 512, num_classes)?;
1290        Ok(Self {
1291            num_classes,
1292            multi_label,
1293            classifier,
1294        })
1295    }
1296}
1297/// Vision task configurations
1298#[derive(Debug, Clone)]
1299pub enum VisionTaskConfig {
1300    /// Image classification
1301    Classification {
1302        num_classes: usize,
1303        multi_label: bool,
1304    },
1305    /// Object detection
1306    ObjectDetection {
1307        num_classes: usize,
1308        anchor_sizes: Vec<(usize, usize)>,
1309        iou_threshold: f64,
1310    },
1311    /// Semantic segmentation
1312    Segmentation {
1313        num_classes: usize,
1314        output_stride: usize,
1315    },
1316    /// Instance segmentation
1317    InstanceSegmentation {
1318        num_classes: usize,
1319        mask_resolution: (usize, usize),
1320    },
1321    /// Feature extraction
1322    FeatureExtraction { feature_dim: usize, normalize: bool },
1323    /// Image generation
1324    Generation {
1325        latent_dim: usize,
1326        output_channels: usize,
1327    },
1328}
1329/// Quantum CNN backbone
1330#[derive(Debug, Clone)]
1331pub struct QuantumCNNBackbone {
1332    /// Convolutional layers
1333    pub conv_layers: Vec<QuantumConvolutionalNN>,
1334    /// Pooling configuration
1335    pub pooling_type: PoolingType,
1336    /// Number of qubits
1337    pub num_qubits: usize,
1338    /// Model parameters
1339    pub parameters: Array1<f64>,
1340}
1341impl QuantumCNNBackbone {
1342    fn new(
1343        conv_configs: Vec<ConvolutionalConfig>,
1344        pooling_type: PoolingType,
1345        num_qubits: usize,
1346    ) -> Result<Self> {
1347        let mut conv_layers = Vec::new();
1348        for config in conv_configs {
1349            let qcnn = QuantumConvolutionalNN::new(vec![], num_qubits, 224, config.num_filters)?;
1350            conv_layers.push(qcnn);
1351        }
1352        Ok(Self {
1353            conv_layers,
1354            pooling_type,
1355            num_qubits,
1356            parameters: Array1::zeros(100),
1357        })
1358    }
1359}
1360/// Training history
1361#[derive(Debug, Clone)]
1362pub struct TrainingHistory {
1363    pub epochs: Vec<usize>,
1364    pub train_losses: Vec<f64>,
1365    pub val_losses: Vec<f64>,
1366    pub metrics: Vec<HashMap<String, f64>>,
1367}
1368impl TrainingHistory {
1369    fn new() -> Self {
1370        Self {
1371            epochs: Vec::new(),
1372            train_losses: Vec::new(),
1373            val_losses: Vec::new(),
1374            metrics: Vec::new(),
1375        }
1376    }
1377    fn add_epoch(
1378        &mut self,
1379        epoch: usize,
1380        train_loss: f64,
1381        val_loss: f64,
1382        metrics: HashMap<String, f64>,
1383    ) {
1384        self.epochs.push(epoch);
1385        self.train_losses.push(train_loss);
1386        self.val_losses.push(val_loss);
1387        self.metrics.push(metrics);
1388    }
1389}
1390/// Residual block configuration
1391#[derive(Debug, Clone)]
1392pub struct ResidualBlock {
1393    /// Number of channels
1394    pub channels: usize,
1395    /// Kernel size
1396    pub kernel_size: usize,
1397    /// Stride
1398    pub stride: usize,
1399    /// Use quantum convolution
1400    pub quantum_conv: bool,
1401}
1402/// Quantum-specific metrics
1403#[derive(Debug, Clone)]
1404pub struct QuantumMetrics {
1405    /// Circuit depth
1406    pub circuit_depth: usize,
1407    /// Entanglement entropy
1408    pub entanglement_entropy: f64,
1409    /// Quantum advantage factor
1410    pub quantum_advantage: f64,
1411    /// Coherence time utilization
1412    pub coherence_utilization: f64,
1413}
1414/// Quantum feature extractor
1415#[derive(Debug, Clone)]
1416pub struct QuantumFeatureExtractor {
1417    /// Feature dimension
1418    pub feature_dim: usize,
1419    /// Quantum circuit parameters for feature extraction
1420    pub feature_circuit_params: Vec<Vec<f64>>,
1421    /// Feature transformation network
1422    pub transform_network: QuantumNeuralNetwork,
1423    /// Attention mechanism
1424    pub attention: QuantumSpatialAttention,
1425}
1426impl QuantumFeatureExtractor {
1427    /// Create new quantum feature extractor
1428    pub fn new(feature_dim: usize, num_qubits: usize) -> Result<Self> {
1429        let mut feature_circuit_params = Vec::new();
1430        for _ in 0..5 {
1431            let mut params = Vec::new();
1432            for _ in 0..num_qubits {
1433                params.push(1.0);
1434                params.push(0.0);
1435            }
1436            for _ in 0..num_qubits - 1 {
1437                params.push(2.0);
1438            }
1439            feature_circuit_params.push(params);
1440        }
1441        let layers = vec![
1442            QNNLayerType::EncodingLayer { num_features: 256 },
1443            QNNLayerType::VariationalLayer {
1444                num_params: feature_dim,
1445            },
1446            QNNLayerType::MeasurementLayer {
1447                measurement_basis: "computational".to_string(),
1448            },
1449        ];
1450        let transform_network = QuantumNeuralNetwork::new(layers, num_qubits, 256, feature_dim)?;
1451        let attention = QuantumSpatialAttention::new(8, 64, num_qubits)?;
1452        Ok(Self {
1453            feature_dim,
1454            feature_circuit_params,
1455            transform_network,
1456            attention,
1457        })
1458    }
1459    /// Extract quantum-enhanced features
1460    pub fn extract(&self, features: &Array4<f64>) -> Result<Array4<f64>> {
1461        let attended = self.attention.apply(features)?;
1462        Ok(attended)
1463    }
1464}
1465
1466#[cfg(test)]
1467mod backward_regression_tests {
1468    use super::*;
1469
1470    /// Minimal test-only backbone whose output scales linearly with its
1471    /// single parameter, so a finite-difference gradient with respect to
1472    /// that parameter is guaranteed to be non-zero.
1473    #[derive(Debug, Clone)]
1474    struct ScalingBackbone {
1475        params: Array1<f64>,
1476    }
1477    impl VisionModel for ScalingBackbone {
1478        fn forward(&self, input: &Array4<f64>) -> Result<Array4<f64>> {
1479            let scale = self.params[0];
1480            Ok(input.mapv(|x| x * scale))
1481        }
1482        fn parameters(&self) -> &Array1<f64> {
1483            &self.params
1484        }
1485        fn update_parameters(&mut self, params: &Array1<f64>) -> Result<()> {
1486            self.params = params.clone();
1487            Ok(())
1488        }
1489        fn num_parameters(&self) -> usize {
1490            self.params.len()
1491        }
1492        fn clone_box(&self) -> Box<dyn VisionModel> {
1493            Box::new(self.clone())
1494        }
1495    }
1496
1497    /// Minimal test-only classification head whose logits scale linearly
1498    /// with its single parameter and the mean input feature value.
1499    #[derive(Debug, Clone)]
1500    struct LinearClassificationHead {
1501        params: Array1<f64>,
1502        num_classes: usize,
1503    }
1504    impl TaskHead for LinearClassificationHead {
1505        fn forward(&self, features: &Array4<f64>) -> Result<TaskOutput> {
1506            let (batch, _, _, _) = features.dim();
1507            let weight = self.params[0];
1508            let mut logits = Array2::<f64>::zeros((batch, self.num_classes));
1509            for b in 0..batch {
1510                let mean_feature = features.slice(s![b, .., .., ..]).mean().unwrap_or(0.0);
1511                for c in 0..self.num_classes {
1512                    // Scale by class index (rather than adding a fixed
1513                    // per-class offset) so the *relative* gap between
1514                    // classes' logits -- and therefore the softmax
1515                    // cross-entropy loss -- actually depends on
1516                    // `mean_feature` and `weight`. (Softmax is shift
1517                    // invariant, so an offset added identically to every
1518                    // class would cancel out and make the loss insensitive
1519                    // to both parameters.)
1520                    logits[[b, c]] = mean_feature * weight * (c as f64);
1521                }
1522            }
1523            Ok(TaskOutput::Classification {
1524                logits: logits.clone(),
1525                probabilities: logits,
1526            })
1527        }
1528        fn parameters(&self) -> &Array1<f64> {
1529            &self.params
1530        }
1531        fn update_parameters(&mut self, params: &Array1<f64>) -> Result<()> {
1532            self.params = params.clone();
1533            Ok(())
1534        }
1535        fn clone_box(&self) -> Box<dyn TaskHead> {
1536            Box::new(self.clone())
1537        }
1538    }
1539
1540    fn build_test_pipeline(backbone_param: f64, head_param: f64) -> QuantumVisionPipeline {
1541        let preprocessing = PreprocessingConfig {
1542            image_size: (2, 2),
1543            normalize: false,
1544            mean: Vec::new(),
1545            std: Vec::new(),
1546            augmentation: AugmentationConfig {
1547                horizontal_flip: false,
1548                rotation_range: 0.0,
1549                zoom_range: (1.0, 1.0),
1550                brightness_range: (1.0, 1.0),
1551                quantum_noise: false,
1552            },
1553            color_space: ColorSpace::Grayscale,
1554        };
1555        let preprocessor = ImagePreprocessor::new(preprocessing);
1556        let encoder = QuantumImageEncoder::new(ImageEncodingMethod::AmplitudeEncoding, 2)
1557            .expect("encoder should construct");
1558        let feature_extractor =
1559            QuantumFeatureExtractor::new(4, 2).expect("feature extractor should construct");
1560
1561        QuantumVisionPipeline {
1562            config: QuantumVisionConfig::default(),
1563            encoder,
1564            backbone: Box::new(ScalingBackbone {
1565                params: Array1::from_vec(vec![backbone_param]),
1566            }),
1567            task_head: Box::new(LinearClassificationHead {
1568                params: Array1::from_vec(vec![head_param]),
1569                num_classes: 2,
1570            }),
1571            feature_extractor,
1572            preprocessor,
1573            metrics: VisionMetrics::new(),
1574            backbone_gradient: None,
1575            task_head_gradient: None,
1576        }
1577    }
1578
1579    /// Regression test for the "backward/update_parameters are no-ops" bug:
1580    /// a real gradient must be estimated and actually applied to both the
1581    /// backbone's and the task head's parameters, changing them.
1582    #[test]
1583    fn backward_and_update_parameters_actually_change_weights() {
1584        let mut pipeline = build_test_pipeline(2.0, 1.0);
1585        let images = Array4::<f64>::from_elem((1, 1, 2, 2), 0.7);
1586        let target = TaskTarget::Classification { labels: vec![1] };
1587
1588        let backbone_before = pipeline.backbone.parameters().clone();
1589        let head_before = pipeline.task_head.parameters().clone();
1590
1591        pipeline
1592            .backward(&images, &target, 0.0)
1593            .expect("backward should succeed");
1594        assert!(pipeline.backbone_gradient.is_some());
1595        assert!(pipeline.task_head_gradient.is_some());
1596
1597        pipeline
1598            .update_parameters(&OptimizationMethod::GradientDescent)
1599            .expect("update_parameters should succeed");
1600
1601        let backbone_after = pipeline.backbone.parameters().clone();
1602        let head_after = pipeline.task_head.parameters().clone();
1603
1604        assert!(
1605            (backbone_before[0] - backbone_after[0]).abs() > 1e-9,
1606            "backbone parameter should have changed: {} -> {}",
1607            backbone_before[0],
1608            backbone_after[0]
1609        );
1610        assert!(
1611            (head_before[0] - head_after[0]).abs() > 1e-9,
1612            "task head parameter should have changed: {} -> {}",
1613            head_before[0],
1614            head_after[0]
1615        );
1616
1617        // The gradients are consumed (cleared) once applied.
1618        assert!(pipeline.backbone_gradient.is_none());
1619        assert!(pipeline.task_head_gradient.is_none());
1620    }
1621
1622    /// A full `train` epoch should reduce the training loss for this
1623    /// trivially learnable toy problem, demonstrating that gradients flow
1624    /// end-to-end through `forward` -> `compute_loss` -> `backward` ->
1625    /// `update_parameters`, not merely that some numbers change.
1626    #[test]
1627    fn train_reduces_loss_over_epochs() {
1628        let mut pipeline = build_test_pipeline(0.1, 0.1);
1629        let images = Array4::<f64>::from_elem((1, 1, 2, 2), 0.9);
1630        let target = TaskTarget::Classification { labels: vec![1] };
1631        let train_data = vec![(images.clone(), target.clone())];
1632        let val_data = vec![(images, target)];
1633
1634        let initial_output = pipeline.forward(&train_data[0].0).expect("forward ok");
1635        let initial_loss = QuantumVisionPipeline::compute_loss(&initial_output, &train_data[0].1)
1636            .expect("loss ok");
1637
1638        let history = pipeline
1639            .train(
1640                &train_data,
1641                &val_data,
1642                20,
1643                OptimizationMethod::GradientDescent,
1644            )
1645            .expect("training should succeed");
1646
1647        let final_loss = *history.train_losses.last().expect("has losses");
1648        assert!(
1649            final_loss < initial_loss,
1650            "expected training loss to decrease: initial={initial_loss}, final={final_loss}"
1651        );
1652    }
1653}