1use crate::error::{MLError, Result};
12use crate::torchquantum::{
13 CType, NParamsEnum, OpHistoryEntry, TQDevice, TQModule, TQOperator, TQParameter, WiresEnum,
14};
15use scirs2_core::ndarray::{Array2, ArrayD, IxDyn};
16
17#[derive(Debug, Clone)]
19pub struct TQiSWAP {
20 inverse: bool,
21 static_mode: bool,
22}
23
24impl TQiSWAP {
25 pub fn new() -> Self {
26 Self {
27 inverse: false,
28 static_mode: false,
29 }
30 }
31}
32
33impl Default for TQiSWAP {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl TQModule for TQiSWAP {
40 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
41 Err(MLError::InvalidConfiguration(
42 "Use apply() instead of forward() for operators".to_string(),
43 ))
44 }
45
46 fn parameters(&self) -> Vec<TQParameter> {
47 Vec::new()
48 }
49
50 fn n_wires(&self) -> Option<usize> {
51 Some(2)
52 }
53
54 fn set_n_wires(&mut self, _n_wires: usize) {}
55
56 fn is_static_mode(&self) -> bool {
57 self.static_mode
58 }
59
60 fn static_on(&mut self) {
61 self.static_mode = true;
62 }
63
64 fn static_off(&mut self) {
65 self.static_mode = false;
66 }
67
68 fn name(&self) -> &str {
69 "iSWAP"
70 }
71}
72
73impl TQOperator for TQiSWAP {
74 fn num_wires(&self) -> WiresEnum {
75 WiresEnum::Fixed(2)
76 }
77
78 fn num_params(&self) -> NParamsEnum {
79 NParamsEnum::Fixed(0)
80 }
81
82 fn get_matrix(&self, _params: Option<&[f64]>) -> Array2<CType> {
83 let i_val = if self.inverse {
84 CType::new(0.0, -1.0)
85 } else {
86 CType::new(0.0, 1.0)
87 };
88
89 Array2::from_shape_vec(
90 (4, 4),
91 vec![
92 CType::new(1.0, 0.0),
93 CType::new(0.0, 0.0),
94 CType::new(0.0, 0.0),
95 CType::new(0.0, 0.0),
96 CType::new(0.0, 0.0),
97 CType::new(0.0, 0.0),
98 i_val,
99 CType::new(0.0, 0.0),
100 CType::new(0.0, 0.0),
101 i_val,
102 CType::new(0.0, 0.0),
103 CType::new(0.0, 0.0),
104 CType::new(0.0, 0.0),
105 CType::new(0.0, 0.0),
106 CType::new(0.0, 0.0),
107 CType::new(1.0, 0.0),
108 ],
109 )
110 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
111 }
112
113 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
114 self.apply_with_params(qdev, wires, None)
115 }
116
117 fn apply_with_params(
118 &mut self,
119 qdev: &mut TQDevice,
120 wires: &[usize],
121 _params: Option<&[f64]>,
122 ) -> Result<()> {
123 if wires.len() < 2 {
124 return Err(MLError::InvalidConfiguration(
125 "iSWAP gate requires exactly 2 wires".to_string(),
126 ));
127 }
128 let matrix = self.get_matrix(None);
129 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
130
131 if qdev.record_op {
132 qdev.record_operation(OpHistoryEntry {
133 name: "iswap".to_string(),
134 wires: wires.to_vec(),
135 params: None,
136 inverse: self.inverse,
137 trainable: false,
138 });
139 }
140
141 Ok(())
142 }
143
144 fn has_params(&self) -> bool {
145 false
146 }
147
148 fn trainable(&self) -> bool {
149 false
150 }
151
152 fn inverse(&self) -> bool {
153 self.inverse
154 }
155
156 fn set_inverse(&mut self, inverse: bool) {
157 self.inverse = inverse;
158 }
159}
160
161#[derive(Debug, Clone)]
163pub struct TQECR {
164 inverse: bool,
165 static_mode: bool,
166}
167
168impl TQECR {
169 pub fn new() -> Self {
170 Self {
171 inverse: false,
172 static_mode: false,
173 }
174 }
175}
176
177impl Default for TQECR {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183impl TQModule for TQECR {
184 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
185 Err(MLError::InvalidConfiguration(
186 "Use apply() instead of forward() for operators".to_string(),
187 ))
188 }
189
190 fn parameters(&self) -> Vec<TQParameter> {
191 Vec::new()
192 }
193
194 fn n_wires(&self) -> Option<usize> {
195 Some(2)
196 }
197
198 fn set_n_wires(&mut self, _n_wires: usize) {}
199
200 fn is_static_mode(&self) -> bool {
201 self.static_mode
202 }
203
204 fn static_on(&mut self) {
205 self.static_mode = true;
206 }
207
208 fn static_off(&mut self) {
209 self.static_mode = false;
210 }
211
212 fn name(&self) -> &str {
213 "ECR"
214 }
215}
216
217impl TQOperator for TQECR {
218 fn num_wires(&self) -> WiresEnum {
219 WiresEnum::Fixed(2)
220 }
221
222 fn num_params(&self) -> NParamsEnum {
223 NParamsEnum::Fixed(0)
224 }
225
226 fn get_matrix(&self, _params: Option<&[f64]>) -> Array2<CType> {
227 let scale = 1.0 / std::f64::consts::SQRT_2;
228 let (sign_i, sign_neg_i) = if self.inverse {
229 (CType::new(0.0, -scale), CType::new(0.0, scale))
230 } else {
231 (CType::new(0.0, scale), CType::new(0.0, -scale))
232 };
233
234 Array2::from_shape_vec(
235 (4, 4),
236 vec![
237 CType::new(0.0, 0.0),
238 CType::new(0.0, 0.0),
239 CType::new(scale, 0.0),
240 sign_i,
241 CType::new(0.0, 0.0),
242 CType::new(0.0, 0.0),
243 sign_i,
244 CType::new(scale, 0.0),
245 CType::new(scale, 0.0),
246 sign_neg_i,
247 CType::new(0.0, 0.0),
248 CType::new(0.0, 0.0),
249 sign_neg_i,
250 CType::new(scale, 0.0),
251 CType::new(0.0, 0.0),
252 CType::new(0.0, 0.0),
253 ],
254 )
255 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
256 }
257
258 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
259 self.apply_with_params(qdev, wires, None)
260 }
261
262 fn apply_with_params(
263 &mut self,
264 qdev: &mut TQDevice,
265 wires: &[usize],
266 _params: Option<&[f64]>,
267 ) -> Result<()> {
268 if wires.len() < 2 {
269 return Err(MLError::InvalidConfiguration(
270 "ECR gate requires exactly 2 wires".to_string(),
271 ));
272 }
273 let matrix = self.get_matrix(None);
274 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
275
276 if qdev.record_op {
277 qdev.record_operation(OpHistoryEntry {
278 name: "ecr".to_string(),
279 wires: wires.to_vec(),
280 params: None,
281 inverse: self.inverse,
282 trainable: false,
283 });
284 }
285
286 Ok(())
287 }
288
289 fn has_params(&self) -> bool {
290 false
291 }
292
293 fn trainable(&self) -> bool {
294 false
295 }
296
297 fn inverse(&self) -> bool {
298 self.inverse
299 }
300
301 fn set_inverse(&mut self, inverse: bool) {
302 self.inverse = inverse;
303 }
304}
305
306#[derive(Debug, Clone)]
308pub struct TQCY {
309 inverse: bool,
310 static_mode: bool,
311}
312
313impl TQCY {
314 pub fn new() -> Self {
315 Self {
316 inverse: false,
317 static_mode: false,
318 }
319 }
320}
321
322impl Default for TQCY {
323 fn default() -> Self {
324 Self::new()
325 }
326}
327
328impl TQModule for TQCY {
329 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
330 Err(MLError::InvalidConfiguration(
331 "Use apply() instead of forward() for operators".to_string(),
332 ))
333 }
334
335 fn parameters(&self) -> Vec<TQParameter> {
336 Vec::new()
337 }
338
339 fn n_wires(&self) -> Option<usize> {
340 Some(2)
341 }
342
343 fn set_n_wires(&mut self, _n_wires: usize) {}
344
345 fn is_static_mode(&self) -> bool {
346 self.static_mode
347 }
348
349 fn static_on(&mut self) {
350 self.static_mode = true;
351 }
352
353 fn static_off(&mut self) {
354 self.static_mode = false;
355 }
356
357 fn name(&self) -> &str {
358 "CY"
359 }
360}
361
362impl TQOperator for TQCY {
363 fn num_wires(&self) -> WiresEnum {
364 WiresEnum::Fixed(2)
365 }
366
367 fn num_params(&self) -> NParamsEnum {
368 NParamsEnum::Fixed(0)
369 }
370
371 fn get_matrix(&self, _params: Option<&[f64]>) -> Array2<CType> {
372 let (i_val, neg_i_val) = if self.inverse {
373 (CType::new(0.0, -1.0), CType::new(0.0, 1.0))
374 } else {
375 (CType::new(0.0, 1.0), CType::new(0.0, -1.0))
376 };
377
378 Array2::from_shape_vec(
379 (4, 4),
380 vec![
381 CType::new(1.0, 0.0),
382 CType::new(0.0, 0.0),
383 CType::new(0.0, 0.0),
384 CType::new(0.0, 0.0),
385 CType::new(0.0, 0.0),
386 CType::new(1.0, 0.0),
387 CType::new(0.0, 0.0),
388 CType::new(0.0, 0.0),
389 CType::new(0.0, 0.0),
390 CType::new(0.0, 0.0),
391 CType::new(0.0, 0.0),
392 neg_i_val,
393 CType::new(0.0, 0.0),
394 CType::new(0.0, 0.0),
395 i_val,
396 CType::new(0.0, 0.0),
397 ],
398 )
399 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
400 }
401
402 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
403 self.apply_with_params(qdev, wires, None)
404 }
405
406 fn apply_with_params(
407 &mut self,
408 qdev: &mut TQDevice,
409 wires: &[usize],
410 _params: Option<&[f64]>,
411 ) -> Result<()> {
412 if wires.len() < 2 {
413 return Err(MLError::InvalidConfiguration(
414 "CY gate requires exactly 2 wires".to_string(),
415 ));
416 }
417 let matrix = self.get_matrix(None);
418 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
419
420 if qdev.record_op {
421 qdev.record_operation(OpHistoryEntry {
422 name: "cy".to_string(),
423 wires: wires.to_vec(),
424 params: None,
425 inverse: self.inverse,
426 trainable: false,
427 });
428 }
429
430 Ok(())
431 }
432
433 fn has_params(&self) -> bool {
434 false
435 }
436
437 fn trainable(&self) -> bool {
438 false
439 }
440
441 fn inverse(&self) -> bool {
442 self.inverse
443 }
444
445 fn set_inverse(&mut self, inverse: bool) {
446 self.inverse = inverse;
447 }
448}
449
450#[derive(Debug, Clone)]
452pub struct TQSSWAP {
453 inverse: bool,
454 static_mode: bool,
455}
456
457impl TQSSWAP {
458 pub fn new() -> Self {
459 Self {
460 inverse: false,
461 static_mode: false,
462 }
463 }
464}
465
466impl Default for TQSSWAP {
467 fn default() -> Self {
468 Self::new()
469 }
470}
471
472impl TQModule for TQSSWAP {
473 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
474 Err(MLError::InvalidConfiguration(
475 "Use apply() instead of forward() for operators".to_string(),
476 ))
477 }
478
479 fn parameters(&self) -> Vec<TQParameter> {
480 Vec::new()
481 }
482
483 fn n_wires(&self) -> Option<usize> {
484 Some(2)
485 }
486
487 fn set_n_wires(&mut self, _n_wires: usize) {}
488
489 fn is_static_mode(&self) -> bool {
490 self.static_mode
491 }
492
493 fn static_on(&mut self) {
494 self.static_mode = true;
495 }
496
497 fn static_off(&mut self) {
498 self.static_mode = false;
499 }
500
501 fn name(&self) -> &str {
502 "SSWAP"
503 }
504}
505
506impl TQOperator for TQSSWAP {
507 fn num_wires(&self) -> WiresEnum {
508 WiresEnum::Fixed(2)
509 }
510
511 fn num_params(&self) -> NParamsEnum {
512 NParamsEnum::Fixed(0)
513 }
514
515 fn get_matrix(&self, _params: Option<&[f64]>) -> Array2<CType> {
516 let (a, b) = if self.inverse {
517 (CType::new(0.5, -0.5), CType::new(0.5, 0.5))
518 } else {
519 (CType::new(0.5, 0.5), CType::new(0.5, -0.5))
520 };
521
522 Array2::from_shape_vec(
523 (4, 4),
524 vec![
525 CType::new(1.0, 0.0),
526 CType::new(0.0, 0.0),
527 CType::new(0.0, 0.0),
528 CType::new(0.0, 0.0),
529 CType::new(0.0, 0.0),
530 a,
531 b,
532 CType::new(0.0, 0.0),
533 CType::new(0.0, 0.0),
534 b,
535 a,
536 CType::new(0.0, 0.0),
537 CType::new(0.0, 0.0),
538 CType::new(0.0, 0.0),
539 CType::new(0.0, 0.0),
540 CType::new(1.0, 0.0),
541 ],
542 )
543 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
544 }
545
546 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
547 self.apply_with_params(qdev, wires, None)
548 }
549
550 fn apply_with_params(
551 &mut self,
552 qdev: &mut TQDevice,
553 wires: &[usize],
554 _params: Option<&[f64]>,
555 ) -> Result<()> {
556 if wires.len() < 2 {
557 return Err(MLError::InvalidConfiguration(
558 "SSWAP gate requires exactly 2 wires".to_string(),
559 ));
560 }
561 let matrix = self.get_matrix(None);
562 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
563
564 if qdev.record_op {
565 qdev.record_operation(OpHistoryEntry {
566 name: "sswap".to_string(),
567 wires: wires.to_vec(),
568 params: None,
569 inverse: self.inverse,
570 trainable: false,
571 });
572 }
573
574 Ok(())
575 }
576
577 fn has_params(&self) -> bool {
578 false
579 }
580
581 fn trainable(&self) -> bool {
582 false
583 }
584
585 fn inverse(&self) -> bool {
586 self.inverse
587 }
588
589 fn set_inverse(&mut self, inverse: bool) {
590 self.inverse = inverse;
591 }
592}
593
594#[derive(Debug, Clone)]
596pub struct TQDCX {
597 inverse: bool,
598 static_mode: bool,
599}
600
601impl TQDCX {
602 pub fn new() -> Self {
603 Self {
604 inverse: false,
605 static_mode: false,
606 }
607 }
608}
609
610impl Default for TQDCX {
611 fn default() -> Self {
612 Self::new()
613 }
614}
615
616impl TQModule for TQDCX {
617 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
618 Err(MLError::InvalidConfiguration(
619 "Use apply() instead of forward() for operators".to_string(),
620 ))
621 }
622
623 fn parameters(&self) -> Vec<TQParameter> {
624 Vec::new()
625 }
626
627 fn n_wires(&self) -> Option<usize> {
628 Some(2)
629 }
630
631 fn set_n_wires(&mut self, _n_wires: usize) {}
632
633 fn is_static_mode(&self) -> bool {
634 self.static_mode
635 }
636
637 fn static_on(&mut self) {
638 self.static_mode = true;
639 }
640
641 fn static_off(&mut self) {
642 self.static_mode = false;
643 }
644
645 fn name(&self) -> &str {
646 "DCX"
647 }
648}
649
650impl TQOperator for TQDCX {
651 fn num_wires(&self) -> WiresEnum {
652 WiresEnum::Fixed(2)
653 }
654
655 fn num_params(&self) -> NParamsEnum {
656 NParamsEnum::Fixed(0)
657 }
658
659 fn get_matrix(&self, _params: Option<&[f64]>) -> Array2<CType> {
660 Array2::from_shape_vec(
661 (4, 4),
662 vec![
663 CType::new(1.0, 0.0),
664 CType::new(0.0, 0.0),
665 CType::new(0.0, 0.0),
666 CType::new(0.0, 0.0),
667 CType::new(0.0, 0.0),
668 CType::new(0.0, 0.0),
669 CType::new(0.0, 0.0),
670 CType::new(1.0, 0.0),
671 CType::new(0.0, 0.0),
672 CType::new(1.0, 0.0),
673 CType::new(0.0, 0.0),
674 CType::new(0.0, 0.0),
675 CType::new(0.0, 0.0),
676 CType::new(0.0, 0.0),
677 CType::new(1.0, 0.0),
678 CType::new(0.0, 0.0),
679 ],
680 )
681 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
682 }
683
684 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
685 self.apply_with_params(qdev, wires, None)
686 }
687
688 fn apply_with_params(
689 &mut self,
690 qdev: &mut TQDevice,
691 wires: &[usize],
692 _params: Option<&[f64]>,
693 ) -> Result<()> {
694 if wires.len() < 2 {
695 return Err(MLError::InvalidConfiguration(
696 "DCX gate requires exactly 2 wires".to_string(),
697 ));
698 }
699 let matrix = self.get_matrix(None);
700 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
701
702 if qdev.record_op {
703 qdev.record_operation(OpHistoryEntry {
704 name: "dcx".to_string(),
705 wires: wires.to_vec(),
706 params: None,
707 inverse: self.inverse,
708 trainable: false,
709 });
710 }
711
712 Ok(())
713 }
714
715 fn has_params(&self) -> bool {
716 false
717 }
718
719 fn trainable(&self) -> bool {
720 false
721 }
722
723 fn inverse(&self) -> bool {
724 self.inverse
725 }
726
727 fn set_inverse(&mut self, inverse: bool) {
728 self.inverse = inverse;
729 }
730}
731
732#[derive(Debug, Clone)]
734pub struct TQXXMinusYY {
735 params: Option<TQParameter>,
736 has_params: bool,
737 trainable: bool,
738 inverse: bool,
739 static_mode: bool,
740}
741
742impl TQXXMinusYY {
743 pub fn new(has_params: bool, trainable: bool) -> Self {
744 let params = if has_params {
745 Some(TQParameter::new(
746 ArrayD::zeros(IxDyn(&[1, 2])),
747 "xxmyy_params",
748 ))
749 } else {
750 None
751 };
752
753 Self {
754 params,
755 has_params,
756 trainable,
757 inverse: false,
758 static_mode: false,
759 }
760 }
761
762 pub fn with_init_params(mut self, theta: f64, beta: f64) -> Self {
763 if let Some(ref mut p) = self.params {
764 p.data[[0, 0]] = theta;
765 p.data[[0, 1]] = beta;
766 }
767 self
768 }
769}
770
771impl Default for TQXXMinusYY {
772 fn default() -> Self {
773 Self::new(true, true)
774 }
775}
776
777impl TQModule for TQXXMinusYY {
778 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
779 Err(MLError::InvalidConfiguration(
780 "Use apply() instead of forward() for operators".to_string(),
781 ))
782 }
783
784 fn parameters(&self) -> Vec<TQParameter> {
785 self.params.iter().cloned().collect()
786 }
787
788 fn n_wires(&self) -> Option<usize> {
789 Some(2)
790 }
791
792 fn set_n_wires(&mut self, _n_wires: usize) {}
793
794 fn is_static_mode(&self) -> bool {
795 self.static_mode
796 }
797
798 fn static_on(&mut self) {
799 self.static_mode = true;
800 }
801
802 fn static_off(&mut self) {
803 self.static_mode = false;
804 }
805
806 fn name(&self) -> &str {
807 "XXMinusYY"
808 }
809
810 fn zero_grad(&mut self) {
811 if let Some(ref mut p) = self.params {
812 p.zero_grad();
813 }
814 }
815}
816
817impl TQOperator for TQXXMinusYY {
818 fn num_wires(&self) -> WiresEnum {
819 WiresEnum::Fixed(2)
820 }
821
822 fn num_params(&self) -> NParamsEnum {
823 NParamsEnum::Fixed(2)
824 }
825
826 fn get_matrix(&self, params: Option<&[f64]>) -> Array2<CType> {
827 let theta = params
828 .and_then(|p| p.first().copied())
829 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 0]]))
830 .unwrap_or(0.0);
831 let beta = params
832 .and_then(|p| p.get(1).copied())
833 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 1]]))
834 .unwrap_or(0.0);
835
836 let theta = if self.inverse { -theta } else { theta };
837 let half_theta = theta / 2.0;
838 let c = half_theta.cos();
839 let s = half_theta.sin();
840
841 let exp_pos = CType::from_polar(1.0, beta);
842 let exp_neg = CType::from_polar(1.0, -beta);
843
844 Array2::from_shape_vec(
845 (4, 4),
846 vec![
847 CType::new(c, 0.0),
848 CType::new(0.0, 0.0),
849 CType::new(0.0, 0.0),
850 CType::new(0.0, -s) * exp_neg,
851 CType::new(0.0, 0.0),
852 CType::new(1.0, 0.0),
853 CType::new(0.0, 0.0),
854 CType::new(0.0, 0.0),
855 CType::new(0.0, 0.0),
856 CType::new(0.0, 0.0),
857 CType::new(1.0, 0.0),
858 CType::new(0.0, 0.0),
859 CType::new(0.0, -s) * exp_pos,
860 CType::new(0.0, 0.0),
861 CType::new(0.0, 0.0),
862 CType::new(c, 0.0),
863 ],
864 )
865 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
866 }
867
868 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
869 self.apply_with_params(qdev, wires, None)
870 }
871
872 fn apply_with_params(
873 &mut self,
874 qdev: &mut TQDevice,
875 wires: &[usize],
876 params: Option<&[f64]>,
877 ) -> Result<()> {
878 if wires.len() < 2 {
879 return Err(MLError::InvalidConfiguration(
880 "XXMinusYY gate requires exactly 2 wires".to_string(),
881 ));
882 }
883 let matrix = self.get_matrix(params);
884 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
885
886 if qdev.record_op {
887 qdev.record_operation(OpHistoryEntry {
888 name: "xxmyy".to_string(),
889 wires: wires.to_vec(),
890 params: params.map(|p| p.to_vec()),
891 inverse: self.inverse,
892 trainable: self.trainable,
893 });
894 }
895
896 Ok(())
897 }
898
899 fn has_params(&self) -> bool {
900 self.has_params
901 }
902
903 fn trainable(&self) -> bool {
904 self.trainable
905 }
906
907 fn inverse(&self) -> bool {
908 self.inverse
909 }
910
911 fn set_inverse(&mut self, inverse: bool) {
912 self.inverse = inverse;
913 }
914}
915
916#[derive(Debug, Clone)]
918pub struct TQXXPlusYY {
919 params: Option<TQParameter>,
920 has_params: bool,
921 trainable: bool,
922 inverse: bool,
923 static_mode: bool,
924}
925
926impl TQXXPlusYY {
927 pub fn new(has_params: bool, trainable: bool) -> Self {
928 let params = if has_params {
929 Some(TQParameter::new(
930 ArrayD::zeros(IxDyn(&[1, 2])),
931 "xxpyy_params",
932 ))
933 } else {
934 None
935 };
936
937 Self {
938 params,
939 has_params,
940 trainable,
941 inverse: false,
942 static_mode: false,
943 }
944 }
945
946 pub fn with_init_params(mut self, theta: f64, beta: f64) -> Self {
947 if let Some(ref mut p) = self.params {
948 p.data[[0, 0]] = theta;
949 p.data[[0, 1]] = beta;
950 }
951 self
952 }
953}
954
955impl Default for TQXXPlusYY {
956 fn default() -> Self {
957 Self::new(true, true)
958 }
959}
960
961impl TQModule for TQXXPlusYY {
962 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
963 Err(MLError::InvalidConfiguration(
964 "Use apply() instead of forward() for operators".to_string(),
965 ))
966 }
967
968 fn parameters(&self) -> Vec<TQParameter> {
969 self.params.iter().cloned().collect()
970 }
971
972 fn n_wires(&self) -> Option<usize> {
973 Some(2)
974 }
975
976 fn set_n_wires(&mut self, _n_wires: usize) {}
977
978 fn is_static_mode(&self) -> bool {
979 self.static_mode
980 }
981
982 fn static_on(&mut self) {
983 self.static_mode = true;
984 }
985
986 fn static_off(&mut self) {
987 self.static_mode = false;
988 }
989
990 fn name(&self) -> &str {
991 "XXPlusYY"
992 }
993
994 fn zero_grad(&mut self) {
995 if let Some(ref mut p) = self.params {
996 p.zero_grad();
997 }
998 }
999}
1000
1001impl TQOperator for TQXXPlusYY {
1002 fn num_wires(&self) -> WiresEnum {
1003 WiresEnum::Fixed(2)
1004 }
1005
1006 fn num_params(&self) -> NParamsEnum {
1007 NParamsEnum::Fixed(2)
1008 }
1009
1010 fn get_matrix(&self, params: Option<&[f64]>) -> Array2<CType> {
1011 let theta = params
1012 .and_then(|p| p.first().copied())
1013 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 0]]))
1014 .unwrap_or(0.0);
1015 let beta = params
1016 .and_then(|p| p.get(1).copied())
1017 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 1]]))
1018 .unwrap_or(0.0);
1019
1020 let theta = if self.inverse { -theta } else { theta };
1021 let half_theta = theta / 2.0;
1022 let c = half_theta.cos();
1023 let s = half_theta.sin();
1024
1025 let exp_pos = CType::from_polar(1.0, beta);
1026 let exp_neg = CType::from_polar(1.0, -beta);
1027
1028 Array2::from_shape_vec(
1029 (4, 4),
1030 vec![
1031 CType::new(1.0, 0.0),
1032 CType::new(0.0, 0.0),
1033 CType::new(0.0, 0.0),
1034 CType::new(0.0, 0.0),
1035 CType::new(0.0, 0.0),
1036 CType::new(c, 0.0),
1037 CType::new(0.0, -s) * exp_neg,
1038 CType::new(0.0, 0.0),
1039 CType::new(0.0, 0.0),
1040 CType::new(0.0, -s) * exp_pos,
1041 CType::new(c, 0.0),
1042 CType::new(0.0, 0.0),
1043 CType::new(0.0, 0.0),
1044 CType::new(0.0, 0.0),
1045 CType::new(0.0, 0.0),
1046 CType::new(1.0, 0.0),
1047 ],
1048 )
1049 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
1050 }
1051
1052 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
1053 self.apply_with_params(qdev, wires, None)
1054 }
1055
1056 fn apply_with_params(
1057 &mut self,
1058 qdev: &mut TQDevice,
1059 wires: &[usize],
1060 params: Option<&[f64]>,
1061 ) -> Result<()> {
1062 if wires.len() < 2 {
1063 return Err(MLError::InvalidConfiguration(
1064 "XXPlusYY gate requires exactly 2 wires".to_string(),
1065 ));
1066 }
1067 let matrix = self.get_matrix(params);
1068 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
1069
1070 if qdev.record_op {
1071 qdev.record_operation(OpHistoryEntry {
1072 name: "xxpyy".to_string(),
1073 wires: wires.to_vec(),
1074 params: params.map(|p| p.to_vec()),
1075 inverse: self.inverse,
1076 trainable: self.trainable,
1077 });
1078 }
1079
1080 Ok(())
1081 }
1082
1083 fn has_params(&self) -> bool {
1084 self.has_params
1085 }
1086
1087 fn trainable(&self) -> bool {
1088 self.trainable
1089 }
1090
1091 fn inverse(&self) -> bool {
1092 self.inverse
1093 }
1094
1095 fn set_inverse(&mut self, inverse: bool) {
1096 self.inverse = inverse;
1097 }
1098}
1099
1100#[derive(Debug, Clone)]
1102pub struct TQCH {
1103 inverse: bool,
1104 static_mode: bool,
1105}
1106
1107impl TQCH {
1108 pub fn new() -> Self {
1109 Self {
1110 inverse: false,
1111 static_mode: false,
1112 }
1113 }
1114}
1115
1116impl Default for TQCH {
1117 fn default() -> Self {
1118 Self::new()
1119 }
1120}
1121
1122impl TQModule for TQCH {
1123 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
1124 Err(MLError::InvalidConfiguration(
1125 "Use apply() instead of forward() for operators".to_string(),
1126 ))
1127 }
1128
1129 fn parameters(&self) -> Vec<TQParameter> {
1130 Vec::new()
1131 }
1132
1133 fn n_wires(&self) -> Option<usize> {
1134 Some(2)
1135 }
1136
1137 fn set_n_wires(&mut self, _n_wires: usize) {}
1138
1139 fn is_static_mode(&self) -> bool {
1140 self.static_mode
1141 }
1142
1143 fn static_on(&mut self) {
1144 self.static_mode = true;
1145 }
1146
1147 fn static_off(&mut self) {
1148 self.static_mode = false;
1149 }
1150
1151 fn name(&self) -> &str {
1152 "CH"
1153 }
1154}
1155
1156impl TQOperator for TQCH {
1157 fn num_wires(&self) -> WiresEnum {
1158 WiresEnum::Fixed(2)
1159 }
1160
1161 fn num_params(&self) -> NParamsEnum {
1162 NParamsEnum::Fixed(0)
1163 }
1164
1165 fn get_matrix(&self, _params: Option<&[f64]>) -> Array2<CType> {
1166 let scale = 1.0 / std::f64::consts::SQRT_2;
1167
1168 Array2::from_shape_vec(
1169 (4, 4),
1170 vec![
1171 CType::new(1.0, 0.0),
1172 CType::new(0.0, 0.0),
1173 CType::new(0.0, 0.0),
1174 CType::new(0.0, 0.0),
1175 CType::new(0.0, 0.0),
1176 CType::new(1.0, 0.0),
1177 CType::new(0.0, 0.0),
1178 CType::new(0.0, 0.0),
1179 CType::new(0.0, 0.0),
1180 CType::new(0.0, 0.0),
1181 CType::new(scale, 0.0),
1182 CType::new(scale, 0.0),
1183 CType::new(0.0, 0.0),
1184 CType::new(0.0, 0.0),
1185 CType::new(scale, 0.0),
1186 CType::new(-scale, 0.0),
1187 ],
1188 )
1189 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
1190 }
1191
1192 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
1193 self.apply_with_params(qdev, wires, None)
1194 }
1195
1196 fn apply_with_params(
1197 &mut self,
1198 qdev: &mut TQDevice,
1199 wires: &[usize],
1200 _params: Option<&[f64]>,
1201 ) -> Result<()> {
1202 if wires.len() < 2 {
1203 return Err(MLError::InvalidConfiguration(
1204 "CH gate requires exactly 2 wires".to_string(),
1205 ));
1206 }
1207 let matrix = self.get_matrix(None);
1208 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
1209
1210 if qdev.record_op {
1211 qdev.record_operation(OpHistoryEntry {
1212 name: "ch".to_string(),
1213 wires: wires.to_vec(),
1214 params: None,
1215 inverse: self.inverse,
1216 trainable: false,
1217 });
1218 }
1219
1220 Ok(())
1221 }
1222
1223 fn has_params(&self) -> bool {
1224 false
1225 }
1226
1227 fn trainable(&self) -> bool {
1228 false
1229 }
1230
1231 fn inverse(&self) -> bool {
1232 self.inverse
1233 }
1234
1235 fn set_inverse(&mut self, inverse: bool) {
1236 self.inverse = inverse;
1237 }
1238}
1239
1240#[derive(Debug, Clone)]
1242pub struct TQCPhase {
1243 params: Option<TQParameter>,
1244 has_params: bool,
1245 trainable: bool,
1246 inverse: bool,
1247 static_mode: bool,
1248}
1249
1250impl TQCPhase {
1251 pub fn new(has_params: bool, trainable: bool) -> Self {
1252 let params = if has_params {
1253 Some(TQParameter::new(
1254 ArrayD::zeros(IxDyn(&[1, 1])),
1255 "cphase_theta",
1256 ))
1257 } else {
1258 None
1259 };
1260
1261 Self {
1262 params,
1263 has_params,
1264 trainable,
1265 inverse: false,
1266 static_mode: false,
1267 }
1268 }
1269
1270 pub fn with_init_params(mut self, theta: f64) -> Self {
1271 if let Some(ref mut p) = self.params {
1272 p.data[[0, 0]] = theta;
1273 }
1274 self
1275 }
1276}
1277
1278impl Default for TQCPhase {
1279 fn default() -> Self {
1280 Self::new(true, true)
1281 }
1282}
1283
1284impl TQModule for TQCPhase {
1285 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
1286 Err(MLError::InvalidConfiguration(
1287 "Use apply() instead of forward() for operators".to_string(),
1288 ))
1289 }
1290
1291 fn parameters(&self) -> Vec<TQParameter> {
1292 self.params.iter().cloned().collect()
1293 }
1294
1295 fn n_wires(&self) -> Option<usize> {
1296 Some(2)
1297 }
1298
1299 fn set_n_wires(&mut self, _n_wires: usize) {}
1300
1301 fn is_static_mode(&self) -> bool {
1302 self.static_mode
1303 }
1304
1305 fn static_on(&mut self) {
1306 self.static_mode = true;
1307 }
1308
1309 fn static_off(&mut self) {
1310 self.static_mode = false;
1311 }
1312
1313 fn name(&self) -> &str {
1314 "CPhase"
1315 }
1316
1317 fn zero_grad(&mut self) {
1318 if let Some(ref mut p) = self.params {
1319 p.zero_grad();
1320 }
1321 }
1322}
1323
1324impl TQOperator for TQCPhase {
1325 fn num_wires(&self) -> WiresEnum {
1326 WiresEnum::Fixed(2)
1327 }
1328
1329 fn num_params(&self) -> NParamsEnum {
1330 NParamsEnum::Fixed(1)
1331 }
1332
1333 fn get_matrix(&self, params: Option<&[f64]>) -> Array2<CType> {
1334 let theta = params
1335 .and_then(|p| p.first().copied())
1336 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 0]]))
1337 .unwrap_or(0.0);
1338
1339 let theta = if self.inverse { -theta } else { theta };
1340 let exp_i = CType::from_polar(1.0, theta);
1341
1342 Array2::from_shape_vec(
1343 (4, 4),
1344 vec![
1345 CType::new(1.0, 0.0),
1346 CType::new(0.0, 0.0),
1347 CType::new(0.0, 0.0),
1348 CType::new(0.0, 0.0),
1349 CType::new(0.0, 0.0),
1350 CType::new(1.0, 0.0),
1351 CType::new(0.0, 0.0),
1352 CType::new(0.0, 0.0),
1353 CType::new(0.0, 0.0),
1354 CType::new(0.0, 0.0),
1355 CType::new(1.0, 0.0),
1356 CType::new(0.0, 0.0),
1357 CType::new(0.0, 0.0),
1358 CType::new(0.0, 0.0),
1359 CType::new(0.0, 0.0),
1360 exp_i,
1361 ],
1362 )
1363 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
1364 }
1365
1366 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
1367 self.apply_with_params(qdev, wires, None)
1368 }
1369
1370 fn apply_with_params(
1371 &mut self,
1372 qdev: &mut TQDevice,
1373 wires: &[usize],
1374 params: Option<&[f64]>,
1375 ) -> Result<()> {
1376 if wires.len() < 2 {
1377 return Err(MLError::InvalidConfiguration(
1378 "CPhase gate requires exactly 2 wires".to_string(),
1379 ));
1380 }
1381 let matrix = self.get_matrix(params);
1382 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
1383
1384 if qdev.record_op {
1385 qdev.record_operation(OpHistoryEntry {
1386 name: "cphase".to_string(),
1387 wires: wires.to_vec(),
1388 params: params.map(|p| p.to_vec()),
1389 inverse: self.inverse,
1390 trainable: self.trainable,
1391 });
1392 }
1393
1394 Ok(())
1395 }
1396
1397 fn has_params(&self) -> bool {
1398 self.has_params
1399 }
1400
1401 fn trainable(&self) -> bool {
1402 self.trainable
1403 }
1404
1405 fn inverse(&self) -> bool {
1406 self.inverse
1407 }
1408
1409 fn set_inverse(&mut self, inverse: bool) {
1410 self.inverse = inverse;
1411 }
1412}
1413
1414pub type TQCU1 = TQCPhase;
1417
1418#[derive(Debug, Clone)]
1432pub struct TQFSimGate {
1433 params: Option<TQParameter>,
1434 has_params: bool,
1435 trainable: bool,
1436 inverse: bool,
1437 static_mode: bool,
1438}
1439
1440impl TQFSimGate {
1441 pub fn new(has_params: bool, trainable: bool) -> Self {
1442 let params = if has_params {
1443 Some(TQParameter::new(
1444 ArrayD::zeros(IxDyn(&[1, 2])),
1445 "fsim_params",
1446 ))
1447 } else {
1448 None
1449 };
1450
1451 Self {
1452 params,
1453 has_params,
1454 trainable,
1455 inverse: false,
1456 static_mode: false,
1457 }
1458 }
1459
1460 pub fn with_init_params(mut self, theta: f64, phi: f64) -> Self {
1462 if let Some(ref mut p) = self.params {
1463 p.data[[0, 0]] = theta;
1464 p.data[[0, 1]] = phi;
1465 }
1466 self
1467 }
1468
1469 pub fn iswap_like() -> Self {
1471 Self::new(true, false).with_init_params(std::f64::consts::FRAC_PI_2, 0.0)
1472 }
1473
1474 pub fn sqrt_iswap() -> Self {
1476 Self::new(true, false).with_init_params(std::f64::consts::FRAC_PI_4, 0.0)
1477 }
1478
1479 pub fn sycamore() -> Self {
1481 Self::new(true, false)
1482 .with_init_params(std::f64::consts::FRAC_PI_2, std::f64::consts::FRAC_PI_6)
1483 }
1484}
1485
1486impl Default for TQFSimGate {
1487 fn default() -> Self {
1488 Self::new(true, true)
1489 }
1490}
1491
1492impl TQModule for TQFSimGate {
1493 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
1494 Err(MLError::InvalidConfiguration(
1495 "Use apply() instead of forward() for operators".to_string(),
1496 ))
1497 }
1498
1499 fn parameters(&self) -> Vec<TQParameter> {
1500 self.params.iter().cloned().collect()
1501 }
1502
1503 fn n_wires(&self) -> Option<usize> {
1504 Some(2)
1505 }
1506
1507 fn set_n_wires(&mut self, _n_wires: usize) {}
1508
1509 fn is_static_mode(&self) -> bool {
1510 self.static_mode
1511 }
1512
1513 fn static_on(&mut self) {
1514 self.static_mode = true;
1515 }
1516
1517 fn static_off(&mut self) {
1518 self.static_mode = false;
1519 }
1520
1521 fn name(&self) -> &str {
1522 "fSim"
1523 }
1524
1525 fn zero_grad(&mut self) {
1526 if let Some(ref mut p) = self.params {
1527 p.zero_grad();
1528 }
1529 }
1530}
1531
1532impl TQOperator for TQFSimGate {
1533 fn num_wires(&self) -> WiresEnum {
1534 WiresEnum::Fixed(2)
1535 }
1536
1537 fn num_params(&self) -> NParamsEnum {
1538 NParamsEnum::Fixed(2)
1539 }
1540
1541 fn get_matrix(&self, params: Option<&[f64]>) -> Array2<CType> {
1542 let theta = params
1543 .and_then(|p| p.first().copied())
1544 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 0]]))
1545 .unwrap_or(0.0);
1546 let phi = params
1547 .and_then(|p| p.get(1).copied())
1548 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 1]]))
1549 .unwrap_or(0.0);
1550
1551 let theta = if self.inverse { -theta } else { theta };
1552 let phi = if self.inverse { -phi } else { phi };
1553
1554 let c = theta.cos();
1555 let s = theta.sin();
1556 let exp_neg_i_phi = CType::from_polar(1.0, -phi);
1557
1558 Array2::from_shape_vec(
1559 (4, 4),
1560 vec![
1561 CType::new(1.0, 0.0),
1562 CType::new(0.0, 0.0),
1563 CType::new(0.0, 0.0),
1564 CType::new(0.0, 0.0),
1565 CType::new(0.0, 0.0),
1566 CType::new(c, 0.0),
1567 CType::new(0.0, -s),
1568 CType::new(0.0, 0.0),
1569 CType::new(0.0, 0.0),
1570 CType::new(0.0, -s),
1571 CType::new(c, 0.0),
1572 CType::new(0.0, 0.0),
1573 CType::new(0.0, 0.0),
1574 CType::new(0.0, 0.0),
1575 CType::new(0.0, 0.0),
1576 exp_neg_i_phi,
1577 ],
1578 )
1579 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
1580 }
1581
1582 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
1583 self.apply_with_params(qdev, wires, None)
1584 }
1585
1586 fn apply_with_params(
1587 &mut self,
1588 qdev: &mut TQDevice,
1589 wires: &[usize],
1590 params: Option<&[f64]>,
1591 ) -> Result<()> {
1592 if wires.len() < 2 {
1593 return Err(MLError::InvalidConfiguration(
1594 "fSim gate requires exactly 2 wires".to_string(),
1595 ));
1596 }
1597 let matrix = self.get_matrix(params);
1598 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
1599
1600 if qdev.record_op {
1601 qdev.record_operation(OpHistoryEntry {
1602 name: "fsim".to_string(),
1603 wires: wires.to_vec(),
1604 params: params.map(|p| p.to_vec()),
1605 inverse: self.inverse,
1606 trainable: self.trainable,
1607 });
1608 }
1609
1610 Ok(())
1611 }
1612
1613 fn has_params(&self) -> bool {
1614 self.has_params
1615 }
1616
1617 fn trainable(&self) -> bool {
1618 self.trainable
1619 }
1620
1621 fn inverse(&self) -> bool {
1622 self.inverse
1623 }
1624
1625 fn set_inverse(&mut self, inverse: bool) {
1626 self.inverse = inverse;
1627 }
1628}
1629
1630#[derive(Debug, Clone)]
1645pub struct TQGivensRotation {
1646 params: Option<TQParameter>,
1647 has_params: bool,
1648 trainable: bool,
1649 inverse: bool,
1650 static_mode: bool,
1651}
1652
1653impl TQGivensRotation {
1654 pub fn new(has_params: bool, trainable: bool) -> Self {
1655 let params = if has_params {
1656 Some(TQParameter::new(
1657 ArrayD::zeros(IxDyn(&[1, 2])),
1658 "givens_params",
1659 ))
1660 } else {
1661 None
1662 };
1663
1664 Self {
1665 params,
1666 has_params,
1667 trainable,
1668 inverse: false,
1669 static_mode: false,
1670 }
1671 }
1672
1673 pub fn with_init_params(mut self, theta: f64, phi: f64) -> Self {
1675 if let Some(ref mut p) = self.params {
1676 p.data[[0, 0]] = theta;
1677 p.data[[0, 1]] = phi;
1678 }
1679 self
1680 }
1681
1682 pub fn real(theta: f64) -> Self {
1684 Self::new(true, false).with_init_params(theta, 0.0)
1685 }
1686
1687 pub fn complex(theta: f64, phi: f64) -> Self {
1689 Self::new(true, false).with_init_params(theta, phi)
1690 }
1691}
1692
1693impl Default for TQGivensRotation {
1694 fn default() -> Self {
1695 Self::new(true, true)
1696 }
1697}
1698
1699impl TQModule for TQGivensRotation {
1700 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
1701 Err(MLError::InvalidConfiguration(
1702 "Use apply() instead of forward() for operators".to_string(),
1703 ))
1704 }
1705
1706 fn parameters(&self) -> Vec<TQParameter> {
1707 self.params.iter().cloned().collect()
1708 }
1709
1710 fn n_wires(&self) -> Option<usize> {
1711 Some(2)
1712 }
1713
1714 fn set_n_wires(&mut self, _n_wires: usize) {}
1715
1716 fn is_static_mode(&self) -> bool {
1717 self.static_mode
1718 }
1719
1720 fn static_on(&mut self) {
1721 self.static_mode = true;
1722 }
1723
1724 fn static_off(&mut self) {
1725 self.static_mode = false;
1726 }
1727
1728 fn name(&self) -> &str {
1729 "GivensRotation"
1730 }
1731
1732 fn zero_grad(&mut self) {
1733 if let Some(ref mut p) = self.params {
1734 p.zero_grad();
1735 }
1736 }
1737}
1738
1739impl TQOperator for TQGivensRotation {
1740 fn num_wires(&self) -> WiresEnum {
1741 WiresEnum::Fixed(2)
1742 }
1743
1744 fn num_params(&self) -> NParamsEnum {
1745 NParamsEnum::Fixed(2)
1746 }
1747
1748 fn get_matrix(&self, params: Option<&[f64]>) -> Array2<CType> {
1749 let theta = params
1750 .and_then(|p| p.first().copied())
1751 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 0]]))
1752 .unwrap_or(0.0);
1753 let phi = params
1754 .and_then(|p| p.get(1).copied())
1755 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 1]]))
1756 .unwrap_or(0.0);
1757
1758 let theta = if self.inverse { -theta } else { theta };
1759 let phi = if self.inverse { -phi } else { phi };
1760
1761 let half_theta = theta / 2.0;
1762 let c = half_theta.cos();
1763 let s = half_theta.sin();
1764
1765 let exp_i_phi = CType::from_polar(1.0, phi);
1766 let exp_neg_i_phi = CType::from_polar(1.0, -phi);
1767
1768 Array2::from_shape_vec(
1769 (4, 4),
1770 vec![
1771 CType::new(1.0, 0.0),
1772 CType::new(0.0, 0.0),
1773 CType::new(0.0, 0.0),
1774 CType::new(0.0, 0.0),
1775 CType::new(0.0, 0.0),
1776 CType::new(c, 0.0),
1777 -exp_i_phi * s,
1778 CType::new(0.0, 0.0),
1779 CType::new(0.0, 0.0),
1780 exp_neg_i_phi * s,
1781 CType::new(c, 0.0),
1782 CType::new(0.0, 0.0),
1783 CType::new(0.0, 0.0),
1784 CType::new(0.0, 0.0),
1785 CType::new(0.0, 0.0),
1786 CType::new(1.0, 0.0),
1787 ],
1788 )
1789 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
1790 }
1791
1792 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
1793 self.apply_with_params(qdev, wires, None)
1794 }
1795
1796 fn apply_with_params(
1797 &mut self,
1798 qdev: &mut TQDevice,
1799 wires: &[usize],
1800 params: Option<&[f64]>,
1801 ) -> Result<()> {
1802 if wires.len() < 2 {
1803 return Err(MLError::InvalidConfiguration(
1804 "GivensRotation gate requires exactly 2 wires".to_string(),
1805 ));
1806 }
1807 let matrix = self.get_matrix(params);
1808 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
1809
1810 if qdev.record_op {
1811 qdev.record_operation(OpHistoryEntry {
1812 name: "givens".to_string(),
1813 wires: wires.to_vec(),
1814 params: params.map(|p| p.to_vec()),
1815 inverse: self.inverse,
1816 trainable: self.trainable,
1817 });
1818 }
1819
1820 Ok(())
1821 }
1822
1823 fn has_params(&self) -> bool {
1824 self.has_params
1825 }
1826
1827 fn trainable(&self) -> bool {
1828 self.trainable
1829 }
1830
1831 fn inverse(&self) -> bool {
1832 self.inverse
1833 }
1834
1835 fn set_inverse(&mut self, inverse: bool) {
1836 self.inverse = inverse;
1837 }
1838}
1839
1840#[derive(Debug, Clone)]
1847pub struct TQControlledRot {
1848 params: Option<TQParameter>,
1849 has_params: bool,
1850 trainable: bool,
1851 inverse: bool,
1852 static_mode: bool,
1853}
1854
1855impl TQControlledRot {
1856 pub fn new(has_params: bool, trainable: bool) -> Self {
1857 let params = if has_params {
1858 Some(TQParameter::new(
1859 ArrayD::zeros(IxDyn(&[1, 3])),
1860 "crot_params",
1861 ))
1862 } else {
1863 None
1864 };
1865
1866 Self {
1867 params,
1868 has_params,
1869 trainable,
1870 inverse: false,
1871 static_mode: false,
1872 }
1873 }
1874
1875 pub fn with_init_params(mut self, theta: f64, phi: f64, omega: f64) -> Self {
1877 if let Some(ref mut p) = self.params {
1878 p.data[[0, 0]] = theta;
1879 p.data[[0, 1]] = phi;
1880 p.data[[0, 2]] = omega;
1881 }
1882 self
1883 }
1884}
1885
1886impl Default for TQControlledRot {
1887 fn default() -> Self {
1888 Self::new(true, true)
1889 }
1890}
1891
1892impl TQModule for TQControlledRot {
1893 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
1894 Err(MLError::InvalidConfiguration(
1895 "Use apply() instead of forward() for operators".to_string(),
1896 ))
1897 }
1898
1899 fn parameters(&self) -> Vec<TQParameter> {
1900 self.params.iter().cloned().collect()
1901 }
1902
1903 fn n_wires(&self) -> Option<usize> {
1904 Some(2)
1905 }
1906
1907 fn set_n_wires(&mut self, _n_wires: usize) {}
1908
1909 fn is_static_mode(&self) -> bool {
1910 self.static_mode
1911 }
1912
1913 fn static_on(&mut self) {
1914 self.static_mode = true;
1915 }
1916
1917 fn static_off(&mut self) {
1918 self.static_mode = false;
1919 }
1920
1921 fn name(&self) -> &str {
1922 "CRot"
1923 }
1924
1925 fn zero_grad(&mut self) {
1926 if let Some(ref mut p) = self.params {
1927 p.zero_grad();
1928 }
1929 }
1930}
1931
1932impl TQOperator for TQControlledRot {
1933 fn num_wires(&self) -> WiresEnum {
1934 WiresEnum::Fixed(2)
1935 }
1936
1937 fn num_params(&self) -> NParamsEnum {
1938 NParamsEnum::Fixed(3)
1939 }
1940
1941 fn get_matrix(&self, params: Option<&[f64]>) -> Array2<CType> {
1942 let theta = params
1943 .and_then(|p| p.first().copied())
1944 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 0]]))
1945 .unwrap_or(0.0);
1946 let phi = params
1947 .and_then(|p| p.get(1).copied())
1948 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 1]]))
1949 .unwrap_or(0.0);
1950 let omega = params
1951 .and_then(|p| p.get(2).copied())
1952 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 2]]))
1953 .unwrap_or(0.0);
1954
1955 let (theta, phi, omega) = if self.inverse {
1956 (-omega, -phi, -theta)
1957 } else {
1958 (theta, phi, omega)
1959 };
1960
1961 let half_theta = theta / 2.0;
1964 let half_phi = phi / 2.0;
1965 let half_omega = omega / 2.0;
1966
1967 let cos_phi = half_phi.cos();
1973 let sin_phi = half_phi.sin();
1974
1975 let u00 = CType::from_polar(cos_phi, -(half_theta + half_omega));
1976 let u01 = CType::from_polar(-sin_phi, -(half_theta - half_omega));
1977 let u10 = CType::from_polar(sin_phi, half_theta - half_omega);
1978 let u11 = CType::from_polar(cos_phi, half_theta + half_omega);
1979
1980 Array2::from_shape_vec(
1981 (4, 4),
1982 vec![
1983 CType::new(1.0, 0.0),
1984 CType::new(0.0, 0.0),
1985 CType::new(0.0, 0.0),
1986 CType::new(0.0, 0.0),
1987 CType::new(0.0, 0.0),
1988 CType::new(1.0, 0.0),
1989 CType::new(0.0, 0.0),
1990 CType::new(0.0, 0.0),
1991 CType::new(0.0, 0.0),
1992 CType::new(0.0, 0.0),
1993 u00,
1994 u01,
1995 CType::new(0.0, 0.0),
1996 CType::new(0.0, 0.0),
1997 u10,
1998 u11,
1999 ],
2000 )
2001 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
2002 }
2003
2004 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
2005 self.apply_with_params(qdev, wires, None)
2006 }
2007
2008 fn apply_with_params(
2009 &mut self,
2010 qdev: &mut TQDevice,
2011 wires: &[usize],
2012 params: Option<&[f64]>,
2013 ) -> Result<()> {
2014 if wires.len() < 2 {
2015 return Err(MLError::InvalidConfiguration(
2016 "CRot gate requires exactly 2 wires".to_string(),
2017 ));
2018 }
2019 let matrix = self.get_matrix(params);
2020 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
2021
2022 if qdev.record_op {
2023 qdev.record_operation(OpHistoryEntry {
2024 name: "crot".to_string(),
2025 wires: wires.to_vec(),
2026 params: params.map(|p| p.to_vec()),
2027 inverse: self.inverse,
2028 trainable: self.trainable,
2029 });
2030 }
2031
2032 Ok(())
2033 }
2034
2035 fn has_params(&self) -> bool {
2036 self.has_params
2037 }
2038
2039 fn trainable(&self) -> bool {
2040 self.trainable
2041 }
2042
2043 fn inverse(&self) -> bool {
2044 self.inverse
2045 }
2046
2047 fn set_inverse(&mut self, inverse: bool) {
2048 self.inverse = inverse;
2049 }
2050}
2051
2052#[derive(Debug, Clone)]
2068pub struct TQPhaseShift2 {
2069 params: Option<TQParameter>,
2070 has_params: bool,
2071 trainable: bool,
2072 inverse: bool,
2073 static_mode: bool,
2074}
2075
2076impl TQPhaseShift2 {
2077 pub fn new(has_params: bool, trainable: bool) -> Self {
2078 let params = if has_params {
2079 Some(TQParameter::new(
2080 ArrayD::zeros(IxDyn(&[1, 1])),
2081 "phaseshift2_phi",
2082 ))
2083 } else {
2084 None
2085 };
2086
2087 Self {
2088 params,
2089 has_params,
2090 trainable,
2091 inverse: false,
2092 static_mode: false,
2093 }
2094 }
2095
2096 pub fn with_init_params(mut self, phi: f64) -> Self {
2098 if let Some(ref mut p) = self.params {
2099 p.data[[0, 0]] = phi;
2100 }
2101 self
2102 }
2103}
2104
2105impl Default for TQPhaseShift2 {
2106 fn default() -> Self {
2107 Self::new(true, true)
2108 }
2109}
2110
2111impl TQModule for TQPhaseShift2 {
2112 fn forward(&mut self, _qdev: &mut TQDevice) -> Result<()> {
2113 Err(MLError::InvalidConfiguration(
2114 "Use apply() instead of forward() for operators".to_string(),
2115 ))
2116 }
2117
2118 fn parameters(&self) -> Vec<TQParameter> {
2119 self.params.iter().cloned().collect()
2120 }
2121
2122 fn n_wires(&self) -> Option<usize> {
2123 Some(2)
2124 }
2125
2126 fn set_n_wires(&mut self, _n_wires: usize) {}
2127
2128 fn is_static_mode(&self) -> bool {
2129 self.static_mode
2130 }
2131
2132 fn static_on(&mut self) {
2133 self.static_mode = true;
2134 }
2135
2136 fn static_off(&mut self) {
2137 self.static_mode = false;
2138 }
2139
2140 fn name(&self) -> &str {
2141 "PhaseShift2"
2142 }
2143
2144 fn zero_grad(&mut self) {
2145 if let Some(ref mut p) = self.params {
2146 p.zero_grad();
2147 }
2148 }
2149}
2150
2151impl TQOperator for TQPhaseShift2 {
2152 fn num_wires(&self) -> WiresEnum {
2153 WiresEnum::Fixed(2)
2154 }
2155
2156 fn num_params(&self) -> NParamsEnum {
2157 NParamsEnum::Fixed(1)
2158 }
2159
2160 fn get_matrix(&self, params: Option<&[f64]>) -> Array2<CType> {
2161 let phi = params
2162 .and_then(|p| p.first().copied())
2163 .or_else(|| self.params.as_ref().map(|p| p.data[[0, 0]]))
2164 .unwrap_or(0.0);
2165
2166 let phi = if self.inverse { -phi } else { phi };
2167 let exp_i_phi = CType::from_polar(1.0, phi);
2168
2169 Array2::from_shape_vec(
2170 (4, 4),
2171 vec![
2172 CType::new(1.0, 0.0),
2173 CType::new(0.0, 0.0),
2174 CType::new(0.0, 0.0),
2175 CType::new(0.0, 0.0),
2176 CType::new(0.0, 0.0),
2177 CType::new(1.0, 0.0),
2178 CType::new(0.0, 0.0),
2179 CType::new(0.0, 0.0),
2180 CType::new(0.0, 0.0),
2181 CType::new(0.0, 0.0),
2182 CType::new(1.0, 0.0),
2183 CType::new(0.0, 0.0),
2184 CType::new(0.0, 0.0),
2185 CType::new(0.0, 0.0),
2186 CType::new(0.0, 0.0),
2187 exp_i_phi,
2188 ],
2189 )
2190 .unwrap_or_else(|_| Array2::eye(4).mapv(|x| CType::new(x, 0.0)))
2191 }
2192
2193 fn apply(&mut self, qdev: &mut TQDevice, wires: &[usize]) -> Result<()> {
2194 self.apply_with_params(qdev, wires, None)
2195 }
2196
2197 fn apply_with_params(
2198 &mut self,
2199 qdev: &mut TQDevice,
2200 wires: &[usize],
2201 params: Option<&[f64]>,
2202 ) -> Result<()> {
2203 if wires.len() < 2 {
2204 return Err(MLError::InvalidConfiguration(
2205 "PhaseShift2 gate requires exactly 2 wires".to_string(),
2206 ));
2207 }
2208 let matrix = self.get_matrix(params);
2209 qdev.apply_two_qubit_gate(wires[0], wires[1], &matrix)?;
2210
2211 if qdev.record_op {
2212 qdev.record_operation(OpHistoryEntry {
2213 name: "phaseshift2".to_string(),
2214 wires: wires.to_vec(),
2215 params: params.map(|p| p.to_vec()),
2216 inverse: self.inverse,
2217 trainable: self.trainable,
2218 });
2219 }
2220
2221 Ok(())
2222 }
2223
2224 fn has_params(&self) -> bool {
2225 self.has_params
2226 }
2227
2228 fn trainable(&self) -> bool {
2229 self.trainable
2230 }
2231
2232 fn inverse(&self) -> bool {
2233 self.inverse
2234 }
2235
2236 fn set_inverse(&mut self, inverse: bool) {
2237 self.inverse = inverse;
2238 }
2239}