1use crate::error::MLError;
7use quantrs2_circuit::prelude::*;
8use quantrs2_sim::optimized_simple::OptimizedStateVector;
9use scirs2_core::Complex64 as Complex;
10use std::f64::consts::PI;
11
12type DMatrix = Vec<Vec<f64>>;
14type DVector<T> = Vec<T>;
15
16#[derive(Debug, Clone)]
18pub struct QuantumConvFilter {
19 pub num_qubits: usize,
21 pub stride: usize,
23 pub params: Vec<f64>,
25}
26
27impl QuantumConvFilter {
28 pub fn new(num_qubits: usize, stride: usize) -> Self {
30 let num_params = num_qubits * 3; let params = vec![0.1; num_params];
33
34 Self {
35 num_qubits,
36 stride,
37 params,
38 }
39 }
40
41 pub fn apply_filter<const N: usize>(
43 &self,
44 circuit: &mut Circuit<N>,
45 start_qubit: usize,
46 ) -> Result<(), MLError> {
47 let end_qubit = (start_qubit + self.num_qubits).min(N);
48
49 let mut param_idx = 0;
51 for i in start_qubit..end_qubit {
52 if param_idx < self.params.len() {
53 circuit.rx(i, self.params[param_idx])?;
54 param_idx += 1;
55 }
56 if param_idx < self.params.len() {
57 circuit.ry(i, self.params[param_idx])?;
58 param_idx += 1;
59 }
60 if param_idx < self.params.len() {
61 circuit.rz(i, self.params[param_idx])?;
62 param_idx += 1;
63 }
64 }
65
66 for i in start_qubit..(end_qubit - 1) {
68 circuit.cnot(i, i + 1)?;
69 }
70
71 Ok(())
72 }
73}
74
75#[derive(Debug, Clone)]
77pub struct QuantumPooling {
78 pub pool_size: usize,
80 pub pool_type: PoolingType,
82}
83
84#[derive(Debug, Clone, Copy)]
85pub enum PoolingType {
86 TraceOut,
88 MeasureReset,
90 Quantum,
92}
93
94impl QuantumPooling {
95 pub fn new(pool_size: usize, pool_type: PoolingType) -> Self {
97 Self {
98 pool_size,
99 pool_type,
100 }
101 }
102
103 pub fn apply_pooling<const N: usize>(
105 &self,
106 circuit: &mut Circuit<N>,
107 active_qubits: &mut Vec<usize>,
108 ) -> Result<(), MLError> {
109 match self.pool_type {
110 PoolingType::TraceOut => {
111 let new_size = active_qubits.len() / self.pool_size;
113 active_qubits.truncate(new_size);
114 }
115 PoolingType::MeasureReset => {
116 let mut new_active = Vec::new();
118 for (i, &qubit) in active_qubits.iter().enumerate() {
119 if i % self.pool_size == 0 {
120 new_active.push(qubit);
121 } else {
122 }
125 }
126 *active_qubits = new_active;
127 }
128 PoolingType::Quantum => {
129 let pool_size = self.pool_size;
131 let new_size = active_qubits.len() / pool_size;
132
133 for i in 0..new_size {
135 let start_idx = i * pool_size;
136 let end_idx = (start_idx + pool_size).min(active_qubits.len());
137
138 if end_idx > start_idx + 1 {
139 for j in start_idx..end_idx - 1 {
141 circuit.cnot(active_qubits[j], active_qubits[j + 1])?;
142 }
143 }
144 }
145
146 active_qubits.truncate(new_size);
148 }
149 }
150 Ok(())
151 }
152}
153
154pub struct QCNN {
156 pub num_qubits: usize,
158 pub conv_layers: Vec<(QuantumConvFilter, QuantumPooling)>,
160 pub fc_params: Vec<f64>,
162}
163
164impl QCNN {
165 pub fn new(
167 num_qubits: usize,
168 conv_filters: Vec<(usize, usize)>, pool_sizes: Vec<usize>,
170 fc_params: usize,
171 ) -> Result<Self, MLError> {
172 if conv_filters.len() != pool_sizes.len() {
173 return Err(MLError::ModelCreationError(
174 "Number of conv filters must match number of pooling layers".to_string(),
175 ));
176 }
177
178 let mut conv_layers = Vec::new();
179 for ((filter_size, stride), pool_size) in conv_filters.into_iter().zip(pool_sizes) {
180 let filter = QuantumConvFilter::new(filter_size, stride);
181 let pooling = QuantumPooling::new(pool_size, PoolingType::TraceOut);
182 conv_layers.push((filter, pooling));
183 }
184
185 let fc_params = vec![0.1; fc_params];
186
187 Ok(Self {
188 num_qubits,
189 conv_layers,
190 fc_params,
191 })
192 }
193
194 pub fn forward(&self, input_state: &DVector<Complex>) -> Result<DVector<Complex>, MLError> {
196 const MAX_QUBITS: usize = 20;
198
199 if self.num_qubits > MAX_QUBITS {
200 return Err(MLError::InvalidParameter(format!(
201 "QCNN supports up to {} qubits",
202 MAX_QUBITS
203 )));
204 }
205
206 let mut circuit = Circuit::<MAX_QUBITS>::new();
207 let mut active_qubits: Vec<usize> = (0..self.num_qubits).collect();
208
209 for (conv_filter, pooling) in &self.conv_layers {
211 let mut pos = 0;
213 while pos + conv_filter.num_qubits <= active_qubits.len() {
214 let start_qubit = active_qubits[pos];
215 conv_filter.apply_filter(&mut circuit, start_qubit)?;
216 pos += conv_filter.stride;
217 }
218
219 pooling.apply_pooling(&mut circuit, &mut active_qubits)?;
221 }
222
223 for (i, &qubit) in active_qubits.iter().enumerate() {
225 if i < self.fc_params.len() {
226 circuit.ry(qubit, self.fc_params[i])?;
227 }
228 }
229
230 let num_qubits = self.num_qubits;
234 let dim = 1usize << num_qubits;
235 let mut simulator = OptimizedStateVector::new(num_qubits);
236
237 {
239 let state = simulator.state_mut();
240 for value in state.iter_mut() {
241 *value = Complex::new(0.0, 0.0);
242 }
243 let mut norm_sq = 0.0;
244 for i in 0..dim.min(input_state.len()) {
245 state[i] = input_state[i];
246 norm_sq += input_state[i].norm_sqr();
247 }
248 if norm_sq > 1e-12 {
249 let inv_norm = 1.0 / norm_sq.sqrt();
250 for value in state.iter_mut() {
251 *value *= inv_norm;
252 }
253 } else {
254 state[0] = Complex::new(1.0, 0.0);
256 }
257 }
258
259 for gate in circuit.gates() {
261 let qubits = gate.qubits();
262 match qubits.len() {
263 1 => {
264 let target = qubits[0].id() as usize;
265 if target >= num_qubits {
266 return Err(MLError::InvalidParameter(format!(
267 "gate targets qubit {target} outside the {num_qubits}-qubit register"
268 )));
269 }
270 let matrix = gate.matrix()?;
271 simulator.apply_single_qubit_gate(&matrix, target);
272 }
273 2 => {
274 let control = qubits[0].id() as usize;
275 let target = qubits[1].id() as usize;
276 if control >= num_qubits || target >= num_qubits {
277 return Err(MLError::InvalidParameter(format!(
278 "two-qubit gate on ({control}, {target}) outside the \
279 {num_qubits}-qubit register"
280 )));
281 }
282 match gate.name() {
283 "CNOT" | "CX" => simulator.apply_cnot(control, target),
284 other => {
285 return Err(MLError::InvalidParameter(format!(
286 "QCNN forward pass does not support two-qubit gate '{other}'"
287 )))
288 }
289 }
290 }
291 other => {
292 return Err(MLError::InvalidParameter(format!(
293 "QCNN forward pass does not support {other}-qubit gates"
294 )))
295 }
296 }
297 }
298
299 let output_size = 1usize << active_qubits.len();
304 let mut probabilities = vec![0.0_f64; output_size];
305 let full_state = simulator.state();
306 for (full_idx, amplitude) in full_state.iter().enumerate() {
307 let mut active_index = 0usize;
308 for (bit, &qubit) in active_qubits.iter().enumerate() {
309 if (full_idx >> qubit) & 1 == 1 {
310 active_index |= 1 << bit;
311 }
312 }
313 probabilities[active_index] += amplitude.norm_sqr();
314 }
315
316 let output = probabilities
317 .into_iter()
318 .map(|p| Complex::new(p.sqrt(), 0.0))
319 .collect();
320
321 Ok(output)
322 }
323
324 pub fn get_parameters(&self) -> Vec<f64> {
326 let mut params = Vec::new();
327
328 for (conv_filter, _) in &self.conv_layers {
329 params.extend(&conv_filter.params);
330 }
331 params.extend(&self.fc_params);
332
333 params
334 }
335
336 pub fn set_parameters(&mut self, params: &[f64]) -> Result<(), MLError> {
338 let mut idx = 0;
339
340 for (conv_filter, _) in &mut self.conv_layers {
341 let filter_params = conv_filter.params.len();
342 if idx + filter_params > params.len() {
343 return Err(MLError::InvalidParameter(
344 "Not enough parameters provided".to_string(),
345 ));
346 }
347 conv_filter
348 .params
349 .copy_from_slice(¶ms[idx..idx + filter_params]);
350 idx += filter_params;
351 }
352
353 let fc_params_len = self.fc_params.len();
354 if idx + fc_params_len > params.len() {
355 return Err(MLError::InvalidParameter(
356 "Not enough parameters for FC layer".to_string(),
357 ));
358 }
359 self.fc_params
360 .copy_from_slice(¶ms[idx..idx + fc_params_len]);
361
362 Ok(())
363 }
364
365 pub fn compute_gradients(
367 &mut self,
368 input_state: &DVector<Complex>,
369 target: &DVector<Complex>,
370 loss_fn: impl Fn(&DVector<Complex>, &DVector<Complex>) -> f64,
371 ) -> Result<Vec<f64>, MLError> {
372 let params = self.get_parameters();
373 let mut gradients = vec![0.0; params.len()];
374 let shift = PI / 2.0;
375
376 for i in 0..params.len() {
377 let mut params_plus = params.clone();
379 params_plus[i] += shift;
380 self.set_parameters(¶ms_plus)?;
381 let output_plus = self.forward(input_state)?;
382 let loss_plus = loss_fn(&output_plus, target);
383
384 let mut params_minus = params.clone();
386 params_minus[i] -= shift;
387 self.set_parameters(¶ms_minus)?;
388 let output_minus = self.forward(input_state)?;
389 let loss_minus = loss_fn(&output_minus, target);
390
391 gradients[i] = (loss_plus - loss_minus) / (2.0 * shift);
393 }
394
395 self.set_parameters(¶ms)?;
397
398 Ok(gradients)
399 }
400}
401
402pub struct QuantumImageEncoder {
404 pub width: usize,
406 pub height: usize,
407 pub num_qubits: usize,
409}
410
411impl QuantumImageEncoder {
412 pub fn new(width: usize, height: usize, num_qubits: usize) -> Self {
414 Self {
415 width,
416 height,
417 num_qubits,
418 }
419 }
420
421 pub fn encode(&self, image: &DMatrix) -> Result<DVector<Complex>, MLError> {
423 if image.len() != self.height || image[0].len() != self.width {
424 return Err(MLError::InvalidParameter(
425 "Image dimensions don't match encoder settings".to_string(),
426 ));
427 }
428
429 let pixels: Vec<f64> = image.iter().flat_map(|row| row.iter()).copied().collect();
431 let norm = pixels.iter().map(|x| x * x).sum::<f64>().sqrt();
432
433 let state_size = 1 << self.num_qubits;
435 let mut state = vec![Complex::new(0.0, 0.0); state_size];
436
437 for (i, &pixel) in pixels.iter().enumerate() {
438 if i < state_size {
439 state[i] = Complex::new(pixel / norm, 0.0);
440 }
441 }
442
443 Ok(state)
444 }
445
446 pub fn decode(&self, state: &DVector<Complex>) -> DMatrix {
448 let mut image = vec![vec![0.0; self.width]; self.height];
449 let mut idx = 0;
450
451 for i in 0..self.height {
452 for j in 0..self.width {
453 if idx < state.len() {
454 image[i][j] = state[idx].norm();
455 idx += 1;
456 }
457 }
458 }
459
460 image
461 }
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467
468 #[test]
469 fn test_qcnn_creation() {
470 let qcnn = QCNN::new(
471 8, vec![(4, 2), (2, 1)], vec![2, 2], 4, )
476 .expect("Failed to create QCNN with valid configuration");
477
478 assert_eq!(qcnn.num_qubits, 8);
479 assert_eq!(qcnn.conv_layers.len(), 2);
480 }
481
482 #[test]
483 fn test_quantum_filter() {
484 let filter = QuantumConvFilter::new(3, 1);
485 assert_eq!(filter.num_qubits, 3);
486 assert_eq!(filter.params.len(), 9); }
488
489 #[test]
490 fn test_filter_application() {
491 let filter = QuantumConvFilter::new(3, 1);
492 let mut circuit = Circuit::<8>::new();
493
494 filter
496 .apply_filter(&mut circuit, 0)
497 .expect("Failed to apply quantum filter to circuit");
498
499 assert!(circuit.num_gates() > 0);
501 }
502
503 #[test]
504 fn test_pooling_trace_out() {
505 let pooling = QuantumPooling::new(2, PoolingType::TraceOut);
506 let mut circuit = Circuit::<8>::new();
507 let mut active_qubits = vec![0, 1, 2, 3, 4, 5, 6, 7];
508
509 pooling
510 .apply_pooling(&mut circuit, &mut active_qubits)
511 .expect("Failed to apply trace-out pooling");
512
513 assert_eq!(active_qubits.len(), 4);
515 }
516
517 #[test]
518 fn test_pooling_measure_reset() {
519 let pooling = QuantumPooling::new(2, PoolingType::MeasureReset);
520 let mut circuit = Circuit::<8>::new();
521 let mut active_qubits = vec![0, 1, 2, 3, 4, 5, 6, 7];
522
523 pooling
524 .apply_pooling(&mut circuit, &mut active_qubits)
525 .expect("Failed to apply measure-reset pooling");
526
527 assert_eq!(active_qubits.len(), 4);
529 assert_eq!(active_qubits, vec![0, 2, 4, 6]);
530 }
531
532 #[test]
533 fn test_image_encoding() {
534 let encoder = QuantumImageEncoder::new(2, 2, 2);
535 let image = vec![vec![0.5, 0.5], vec![0.5, 0.5]];
536
537 let encoded = encoder.encode(&image).expect("Failed to encode image");
538 assert_eq!(encoded.len(), 4); let norm: f64 = encoded.iter().map(|c| c.norm_sqr()).sum();
542 assert!((norm - 1.0).abs() < 1e-10);
543 }
544
545 #[test]
546 fn test_image_decode() {
547 let encoder = QuantumImageEncoder::new(2, 2, 2);
548 let state = vec![
549 Complex::new(0.5, 0.0),
550 Complex::new(0.5, 0.0),
551 Complex::new(0.5, 0.0),
552 Complex::new(0.5, 0.0),
553 ];
554
555 let decoded = encoder.decode(&state);
556 assert_eq!(decoded.len(), 2);
557 assert_eq!(decoded[0].len(), 2);
558 }
559
560 #[test]
561 fn test_qcnn_forward() {
562 let qcnn = QCNN::new(
563 4, vec![(2, 1)], vec![2], 2, )
568 .expect("Failed to create QCNN");
569
570 let input_state = vec![Complex::new(1.0, 0.0); 16]; let output = qcnn.forward(&input_state).expect("Failed to forward pass");
572
573 assert!(!output.is_empty());
575 }
576
577 #[test]
578 fn test_parameter_management() {
579 let mut qcnn = QCNN::new(
580 4, vec![(2, 1)], vec![2], 2, )
585 .expect("Failed to create QCNN");
586
587 let params = qcnn.get_parameters();
588 let num_params = params.len();
589
590 let new_params: Vec<f64> = (0..num_params).map(|i| i as f64 * 0.1).collect();
592 qcnn.set_parameters(&new_params)
593 .expect("Failed to set parameters");
594
595 let retrieved_params = qcnn.get_parameters();
596 assert_eq!(retrieved_params, new_params);
597 }
598
599 #[test]
600 fn test_gradient_computation() {
601 let mut qcnn = QCNN::new(
602 4, vec![(2, 1)], vec![2], 2, )
607 .expect("Failed to create QCNN");
608
609 let input_state = vec![Complex::new(0.5, 0.0); 16];
610 let target_state = vec![Complex::new(0.707, 0.0); 2];
611
612 let loss_fn = |output: &DVector<Complex>, target: &DVector<Complex>| -> f64 {
614 output
615 .iter()
616 .zip(target.iter())
617 .map(|(o, t)| (o - t).norm_sqr())
618 .sum::<f64>()
619 };
620
621 let gradients = qcnn
622 .compute_gradients(&input_state, &target_state, loss_fn)
623 .expect("Failed to compute gradients");
624
625 assert_eq!(gradients.len(), qcnn.get_parameters().len());
627 }
628
629 #[test]
630 fn test_invalid_layer_configuration() {
631 let result = QCNN::new(
633 8,
634 vec![(4, 2), (2, 1)], vec![2], 4,
637 );
638
639 assert!(result.is_err());
640 }
641
642 #[test]
643 fn test_stride_behavior() {
644 let filter = QuantumConvFilter::new(2, 2); assert_eq!(filter.stride, 2);
646
647 let mut circuit = Circuit::<8>::new();
648
649 filter
651 .apply_filter(&mut circuit, 0)
652 .expect("Failed to apply filter at position 0");
653 filter
654 .apply_filter(&mut circuit, 2)
655 .expect("Failed to apply filter at position 2"); }
657
658 #[test]
659 fn test_large_image_encoding() {
660 let encoder = QuantumImageEncoder::new(4, 4, 4); let image = vec![vec![0.25; 4]; 4];
662
663 let encoded = encoder.encode(&image).expect("Failed to encode 4x4 image");
664 assert_eq!(encoded.len(), 16); let decoded = encoder.decode(&encoded);
668 assert_eq!(decoded.len(), 4);
669 assert_eq!(decoded[0].len(), 4);
670 }
671}