1use super::{create_entangling_gates, EntanglementPattern, QMLLayer};
7use crate::{
8 error::{QuantRS2Error, QuantRS2Result},
9 gate::GateOp,
10 parametric::{ParametricRotationX, ParametricRotationY, ParametricRotationZ},
11 qubit::QubitId,
12};
13use scirs2_core::ndarray::Array1;
14use scirs2_core::Complex64;
15use std::f64::consts::PI;
16
17#[derive(Debug, Clone)]
19pub struct Parameter {
20 pub name: String,
21 pub value: f64,
22 pub bounds: Option<(f64, f64)>,
23}
24
25type RXGate = ParametricRotationX;
27type RYGate = ParametricRotationY;
28type RZGate = ParametricRotationZ;
29
30#[derive(Debug, Clone, Copy)]
32struct CNOT {
33 control: QubitId,
34 target: QubitId,
35}
36
37impl GateOp for CNOT {
38 fn name(&self) -> &'static str {
39 "CNOT"
40 }
41
42 fn qubits(&self) -> Vec<QubitId> {
43 vec![self.control, self.target]
44 }
45
46 fn matrix(&self) -> crate::error::QuantRS2Result<Vec<Complex64>> {
47 Ok(vec![
48 Complex64::new(1.0, 0.0),
49 Complex64::new(0.0, 0.0),
50 Complex64::new(0.0, 0.0),
51 Complex64::new(0.0, 0.0),
52 Complex64::new(0.0, 0.0),
53 Complex64::new(1.0, 0.0),
54 Complex64::new(0.0, 0.0),
55 Complex64::new(0.0, 0.0),
56 Complex64::new(0.0, 0.0),
57 Complex64::new(0.0, 0.0),
58 Complex64::new(0.0, 0.0),
59 Complex64::new(1.0, 0.0),
60 Complex64::new(0.0, 0.0),
61 Complex64::new(0.0, 0.0),
62 Complex64::new(1.0, 0.0),
63 Complex64::new(0.0, 0.0),
64 ])
65 }
66
67 fn as_any(&self) -> &dyn std::any::Any {
68 self
69 }
70
71 fn clone_gate(&self) -> Box<dyn GateOp> {
72 Box::new(*self)
73 }
74}
75
76#[derive(Debug, Clone)]
78pub struct RotationLayer {
79 num_qubits: usize,
81 axes: Vec<char>,
83 parameters: Vec<Parameter>,
85 name: String,
87}
88
89impl RotationLayer {
90 pub fn new(num_qubits: usize, axes: Vec<char>) -> QuantRS2Result<Self> {
92 if axes.len() != num_qubits {
93 return Err(QuantRS2Error::InvalidInput(format!(
94 "Expected {} axes, got {}",
95 num_qubits,
96 axes.len()
97 )));
98 }
99
100 for &axis in &axes {
101 if !['X', 'Y', 'Z'].contains(&axis) {
102 return Err(QuantRS2Error::InvalidInput(format!(
103 "Invalid rotation axis: {axis}"
104 )));
105 }
106 }
107
108 let parameters = (0..num_qubits)
109 .map(|i| Parameter {
110 name: format!("rot_{}_{}", axes[i], i),
111 value: 0.0,
112 bounds: Some((-2.0 * PI, 2.0 * PI)),
113 })
114 .collect();
115
116 let name = format!("RotationLayer_{}", axes.iter().collect::<String>());
117
118 Ok(Self {
119 num_qubits,
120 axes,
121 parameters,
122 name,
123 })
124 }
125
126 pub fn uniform(num_qubits: usize, axis: char) -> QuantRS2Result<Self> {
128 Self::new(num_qubits, vec![axis; num_qubits])
129 }
130}
131
132impl QMLLayer for RotationLayer {
133 fn num_qubits(&self) -> usize {
134 self.num_qubits
135 }
136
137 fn parameters(&self) -> &[Parameter] {
138 &self.parameters
139 }
140
141 fn parameters_mut(&mut self) -> &mut [Parameter] {
142 &mut self.parameters
143 }
144
145 fn gates(&self) -> Vec<Box<dyn GateOp>> {
146 self.parameters
147 .iter()
148 .enumerate()
149 .map(|(i, param)| {
150 let qubit = QubitId(i as u32);
151 let gate: Box<dyn GateOp> = match self.axes[i] {
152 'X' => Box::new(ParametricRotationX::new(qubit, param.value)),
153 'Y' => Box::new(ParametricRotationY::new(qubit, param.value)),
154 'Z' => Box::new(ParametricRotationZ::new(qubit, param.value)),
155 _ => unreachable!(),
156 };
157 gate
158 })
159 .collect()
160 }
161
162 fn compute_gradients(
163 &self,
164 state: &Array1<Complex64>,
165 loss_gradient: &Array1<Complex64>,
166 ) -> QuantRS2Result<Vec<f64>> {
167 let shift = std::f64::consts::PI / 2.0;
178 let mut gradients = Vec::with_capacity(self.parameters.len());
179
180 for (i, param) in self.parameters.iter().enumerate() {
181 let theta = param.value;
182 let qubit_idx = i;
190 if qubit_idx >= state.len() {
191 gradients.push(0.0);
192 continue;
193 }
194
195 let cos_shift = (theta + shift).cos() - (theta - shift).cos(); let sin_shift = (theta + shift).sin() - (theta - shift).sin(); let amp = state[qubit_idx];
204 let lg = loss_gradient[qubit_idx];
205 let d_amp = Complex64::new(
207 cos_shift / 2.0 * amp.re - sin_shift / 2.0 * amp.im,
208 cos_shift / 2.0 * amp.im + sin_shift / 2.0 * amp.re,
209 );
210 let grad = lg.re * d_amp.re + lg.im * d_amp.im;
211 gradients.push(grad);
212 }
213
214 Ok(gradients)
215 }
216
217 fn name(&self) -> &str {
218 &self.name
219 }
220}
221
222#[derive(Debug, Clone)]
224pub struct EntanglingLayer {
225 num_qubits: usize,
227 pattern: EntanglementPattern,
229 parameters: Vec<Parameter>,
231 parameterized: bool,
233 name: String,
235}
236
237impl EntanglingLayer {
238 pub fn new(num_qubits: usize, pattern: EntanglementPattern) -> Self {
240 let name = format!("EntanglingLayer_{pattern:?}");
241
242 Self {
243 num_qubits,
244 pattern,
245 parameters: vec![],
246 parameterized: false,
247 name,
248 }
249 }
250
251 pub fn parameterized(num_qubits: usize, pattern: EntanglementPattern) -> Self {
253 let pairs = create_entangling_gates(num_qubits, pattern);
254 let parameters = pairs
255 .iter()
256 .enumerate()
257 .map(|(_i, (ctrl, tgt))| Parameter {
258 name: format!("entangle_{}_{}", ctrl.0, tgt.0),
259 value: 0.0,
260 bounds: Some((-PI, PI)),
261 })
262 .collect();
263
264 let name = format!("ParameterizedEntanglingLayer_{pattern:?}");
265
266 Self {
267 num_qubits,
268 pattern,
269 parameters,
270 parameterized: true,
271 name,
272 }
273 }
274}
275
276impl QMLLayer for EntanglingLayer {
277 fn num_qubits(&self) -> usize {
278 self.num_qubits
279 }
280
281 fn parameters(&self) -> &[Parameter] {
282 &self.parameters
283 }
284
285 fn parameters_mut(&mut self) -> &mut [Parameter] {
286 &mut self.parameters
287 }
288
289 fn gates(&self) -> Vec<Box<dyn GateOp>> {
290 let pairs = create_entangling_gates(self.num_qubits, self.pattern);
291
292 if self.parameterized {
293 pairs
296 .iter()
297 .zip(self.parameters.iter())
298 .map(|((ctrl, tgt), param)| {
299 Box::new(crate::gate::multi::CRZ {
300 control: *ctrl,
301 target: *tgt,
302 theta: param.value,
303 }) as Box<dyn GateOp>
304 })
305 .collect()
306 } else {
307 pairs
309 .iter()
310 .map(|(ctrl, tgt)| {
311 Box::new(CNOT {
312 control: *ctrl,
313 target: *tgt,
314 }) as Box<dyn GateOp>
315 })
316 .collect()
317 }
318 }
319
320 fn compute_gradients(
321 &self,
322 _state: &Array1<Complex64>,
323 _loss_gradient: &Array1<Complex64>,
324 ) -> QuantRS2Result<Vec<f64>> {
325 if self.parameterized {
326 Ok(vec![0.0; self.parameters.len()])
328 } else {
329 Ok(vec![])
331 }
332 }
333
334 fn name(&self) -> &str {
335 &self.name
336 }
337}
338
339#[derive(Debug, Clone)]
341pub struct StronglyEntanglingLayer {
342 num_qubits: usize,
344 rotation_layers: Vec<RotationLayer>,
346 entangling_layer: EntanglingLayer,
348 total_parameters: usize,
350 name: String,
352}
353
354impl StronglyEntanglingLayer {
355 pub fn new(num_qubits: usize, pattern: EntanglementPattern) -> QuantRS2Result<Self> {
357 let rotation_layers = vec![
358 RotationLayer::uniform(num_qubits, 'X')?,
359 RotationLayer::uniform(num_qubits, 'Y')?,
360 RotationLayer::uniform(num_qubits, 'Z')?,
361 ];
362
363 let entangling_layer = EntanglingLayer::new(num_qubits, pattern);
364
365 let total_parameters = rotation_layers
366 .iter()
367 .map(|layer| layer.parameters().len())
368 .sum::<usize>()
369 + entangling_layer.parameters().len();
370
371 let name = format!("StronglyEntanglingLayer_{pattern:?}");
372
373 Ok(Self {
374 num_qubits,
375 rotation_layers,
376 entangling_layer,
377 total_parameters,
378 name,
379 })
380 }
381}
382
383impl QMLLayer for StronglyEntanglingLayer {
384 fn num_qubits(&self) -> usize {
385 self.num_qubits
386 }
387
388 fn parameters(&self) -> &[Parameter] {
389 &[]
392 }
393
394 fn parameters_mut(&mut self) -> &mut [Parameter] {
395 &mut []
397 }
398
399 fn set_parameters(&mut self, values: &[f64]) -> QuantRS2Result<()> {
400 if values.len() != self.total_parameters {
401 return Err(QuantRS2Error::InvalidInput(format!(
402 "Expected {} parameters, got {}",
403 self.total_parameters,
404 values.len()
405 )));
406 }
407
408 let mut offset = 0;
409 for layer in &mut self.rotation_layers {
410 let n = layer.parameters().len();
411 layer.set_parameters(&values[offset..offset + n])?;
412 offset += n;
413 }
414
415 if self.entangling_layer.parameterized {
416 self.entangling_layer.set_parameters(&values[offset..])?;
417 }
418
419 Ok(())
420 }
421
422 fn gates(&self) -> Vec<Box<dyn GateOp>> {
423 let mut gates = Vec::new();
424
425 for layer in &self.rotation_layers {
427 gates.extend(layer.gates());
428 }
429
430 gates.extend(self.entangling_layer.gates());
432
433 gates
434 }
435
436 fn compute_gradients(
437 &self,
438 state: &Array1<Complex64>,
439 loss_gradient: &Array1<Complex64>,
440 ) -> QuantRS2Result<Vec<f64>> {
441 let mut gradients = Vec::new();
442
443 for layer in &self.rotation_layers {
444 gradients.extend(layer.compute_gradients(state, loss_gradient)?);
445 }
446
447 if self.entangling_layer.parameterized {
448 gradients.extend(
449 self.entangling_layer
450 .compute_gradients(state, loss_gradient)?,
451 );
452 }
453
454 Ok(gradients)
455 }
456
457 fn name(&self) -> &str {
458 &self.name
459 }
460}
461
462#[derive(Debug, Clone)]
464pub struct HardwareEfficientLayer {
465 num_qubits: usize,
467 single_qubit_gates: Vec<RotationLayer>,
469 entangling_gates: EntanglingLayer,
471 name: String,
473}
474
475impl HardwareEfficientLayer {
476 pub fn new(num_qubits: usize) -> QuantRS2Result<Self> {
478 let single_qubit_gates = vec![
480 RotationLayer::uniform(num_qubits, 'Y')?,
481 RotationLayer::uniform(num_qubits, 'Z')?,
482 ];
483
484 let entangling_gates = EntanglingLayer::new(num_qubits, EntanglementPattern::Linear);
486
487 Ok(Self {
488 num_qubits,
489 single_qubit_gates,
490 entangling_gates,
491 name: "HardwareEfficientLayer".to_string(),
492 })
493 }
494}
495
496impl QMLLayer for HardwareEfficientLayer {
497 fn num_qubits(&self) -> usize {
498 self.num_qubits
499 }
500
501 fn parameters(&self) -> &[Parameter] {
502 &[]
504 }
505
506 fn parameters_mut(&mut self) -> &mut [Parameter] {
507 &mut []
508 }
509
510 fn gates(&self) -> Vec<Box<dyn GateOp>> {
511 let mut gates = Vec::new();
512
513 for layer in &self.single_qubit_gates {
514 gates.extend(layer.gates());
515 }
516
517 gates.extend(self.entangling_gates.gates());
518
519 gates
520 }
521
522 fn compute_gradients(
523 &self,
524 state: &Array1<Complex64>,
525 loss_gradient: &Array1<Complex64>,
526 ) -> QuantRS2Result<Vec<f64>> {
527 let mut gradients = Vec::new();
528
529 for layer in &self.single_qubit_gates {
530 gradients.extend(layer.compute_gradients(state, loss_gradient)?);
531 }
532
533 Ok(gradients)
534 }
535
536 fn name(&self) -> &str {
537 &self.name
538 }
539}
540
541#[derive(Debug, Clone)]
543pub struct QuantumPoolingLayer {
544 input_qubits: usize,
546 output_qubits: usize,
548 strategy: PoolingStrategy,
550 parameters: Vec<Parameter>,
552 name: String,
554}
555
556#[derive(Debug, Clone, Copy)]
557pub enum PoolingStrategy {
558 TraceOut,
560 MeasureCondition,
562 Parameterized,
564}
565
566impl QuantumPoolingLayer {
567 pub fn new(input_qubits: usize, strategy: PoolingStrategy) -> Self {
573 let output_qubits = input_qubits / 2;
574
575 let parameters = match strategy {
576 PoolingStrategy::Parameterized => (0..output_qubits)
577 .map(|pair| Parameter {
578 name: format!("pool_{pair}"),
579 value: 0.0,
580 bounds: Some((-PI, PI)),
581 })
582 .collect(),
583 PoolingStrategy::TraceOut | PoolingStrategy::MeasureCondition => Vec::new(),
584 };
585
586 Self {
587 input_qubits,
588 output_qubits,
589 strategy,
590 parameters,
591 name: format!("QuantumPoolingLayer_{strategy:?}"),
592 }
593 }
594}
595
596impl QMLLayer for QuantumPoolingLayer {
597 fn num_qubits(&self) -> usize {
598 self.input_qubits
599 }
600
601 fn parameters(&self) -> &[Parameter] {
602 &self.parameters
603 }
604
605 fn parameters_mut(&mut self) -> &mut [Parameter] {
606 &mut self.parameters
607 }
608
609 fn gates(&self) -> Vec<Box<dyn GateOp>> {
610 match self.strategy {
611 PoolingStrategy::Parameterized => self
615 .parameters
616 .iter()
617 .enumerate()
618 .map(|(pair, param)| {
619 let retained = QubitId((2 * pair) as u32);
620 let discarded = QubitId((2 * pair + 1) as u32);
621 Box::new(crate::gate::multi::CRZ {
622 control: discarded,
623 target: retained,
624 theta: param.value,
625 }) as Box<dyn GateOp>
626 })
627 .collect(),
628 PoolingStrategy::TraceOut | PoolingStrategy::MeasureCondition => Vec::new(),
635 }
636 }
637
638 fn compute_gradients(
639 &self,
640 state: &Array1<Complex64>,
641 loss_gradient: &Array1<Complex64>,
642 ) -> QuantRS2Result<Vec<f64>> {
643 if self.parameters.is_empty() {
646 return Ok(Vec::new());
647 }
648
649 let num_qubits = self.input_qubits.max(1);
653 let dim = 1usize << num_qubits;
654 if state.len() != dim || loss_gradient.len() != dim {
655 return Err(QuantRS2Error::InvalidInput(format!(
656 "pooling gradient expects state/loss vectors of length {dim}"
657 )));
658 }
659
660 let shift = PI / 2.0;
661 let mut gradients = Vec::with_capacity(self.parameters.len());
662 for (pair, param) in self.parameters.iter().enumerate() {
663 let retained = QubitId((2 * pair) as u32);
664 let discarded = QubitId((2 * pair + 1) as u32);
665
666 let mut state_plus = state.clone();
667 let gate_plus = crate::gate::multi::CRZ {
668 control: discarded,
669 target: retained,
670 theta: param.value + shift,
671 };
672 crate::qml::simulator::apply_gate(&mut state_plus, &gate_plus)?;
673
674 let mut state_minus = state.clone();
675 let gate_minus = crate::gate::multi::CRZ {
676 control: discarded,
677 target: retained,
678 theta: param.value - shift,
679 };
680 crate::qml::simulator::apply_gate(&mut state_minus, &gate_minus)?;
681
682 let grad: f64 = loss_gradient
684 .iter()
685 .zip(state_plus.iter().zip(state_minus.iter()))
686 .map(|(g, (p, m))| (g.conj() * (p - m)).re)
687 .sum::<f64>()
688 / 2.0;
689 gradients.push(grad);
690 }
691
692 Ok(gradients)
693 }
694
695 fn name(&self) -> &str {
696 &self.name
697 }
698}
699
700#[cfg(test)]
701mod tests {
702 use super::*;
703
704 #[test]
705 fn test_rotation_layer() {
706 let layer = RotationLayer::uniform(3, 'X').expect("rotation layer creation should succeed");
707 assert_eq!(layer.num_qubits(), 3);
708 assert_eq!(layer.parameters().len(), 3);
709
710 let gates = layer.gates();
711 assert_eq!(gates.len(), 3);
712 }
713
714 #[test]
715 fn test_entangling_layer() {
716 let layer = EntanglingLayer::new(4, EntanglementPattern::Linear);
717 assert_eq!(layer.num_qubits(), 4);
718
719 let gates = layer.gates();
720 assert_eq!(gates.len(), 3); }
722
723 #[test]
724 fn test_strongly_entangling_layer() {
725 let layer = StronglyEntanglingLayer::new(2, EntanglementPattern::Full)
726 .expect("strongly entangling layer creation should succeed");
727 assert_eq!(layer.num_qubits(), 2);
728
729 let gates = layer.gates();
730 assert_eq!(gates.len(), 7); }
732
733 #[test]
734 fn test_hardware_efficient_layer() {
735 let layer = HardwareEfficientLayer::new(3)
736 .expect("hardware efficient layer creation should succeed");
737 assert_eq!(layer.num_qubits(), 3);
738
739 let gates = layer.gates();
740 assert_eq!(gates.len(), 8); }
742
743 #[test]
744 fn test_parameterized_entangling_layer_uses_crz_not_cnot() {
745 let mut layer = EntanglingLayer::parameterized(3, EntanglementPattern::Linear);
749 let params = layer.parameters_mut();
750 for (i, p) in params.iter_mut().enumerate() {
751 p.value = 0.5 * (i as f64 + 1.0);
752 }
753 let gates = layer.gates();
754 assert!(!gates.is_empty());
755 for gate in &gates {
756 assert_eq!(
757 gate.name(),
758 "CRZ",
759 "parameterized entangling layer must emit CRZ gates"
760 );
761 }
762 let crz_matrix = gates[0].matrix().expect("crz matrix");
764 let cnot = crate::gate::multi::CNOT {
765 control: QubitId(0),
766 target: QubitId(1),
767 };
768 let cnot_matrix = cnot.matrix().expect("cnot matrix");
769 let differs = crz_matrix
770 .iter()
771 .zip(cnot_matrix.iter())
772 .any(|(a, b)| (a - b).norm() > 1e-9);
773 assert!(differs, "CRZ gate must not equal CNOT");
774 }
775
776 #[test]
777 fn test_parameterized_pooling_layer_emits_gates() {
778 let mut layer = QuantumPoolingLayer::new(4, PoolingStrategy::Parameterized);
781 assert_eq!(layer.parameters().len(), 2); for (i, p) in layer.parameters_mut().iter_mut().enumerate() {
783 p.value = 0.3 * (i as f64 + 1.0);
784 }
785 let gates = layer.gates();
786 assert_eq!(gates.len(), 2, "one pooling gate per pooled pair");
787 for gate in &gates {
788 assert_eq!(gate.name(), "CRZ");
789 }
790 }
791
792 #[test]
793 fn test_traceout_pooling_layer_has_no_gates_legit() {
794 let layer = QuantumPoolingLayer::new(4, PoolingStrategy::TraceOut);
796 assert!(layer.parameters().is_empty());
797 assert!(layer.gates().is_empty());
798 assert!(layer
799 .compute_gradients(&Array1::zeros(16), &Array1::zeros(16))
800 .expect("gradients")
801 .is_empty());
802 }
803}