1use num_complex::Complex64;
2use std::ops::{Add, Mul, Sub};
3
4use crate::error::QuantRS2Result;
5use crate::gate::GateOp;
6use crate::qubit::QubitId;
7
8#[derive(Debug, Clone)]
10pub enum Parameter {
11 Constant(f64),
13
14 Symbol(SymbolicParameter),
16}
17
18impl Parameter {
19 pub fn constant(value: f64) -> Self {
21 Parameter::Constant(value)
22 }
23
24 pub fn symbol(name: &str) -> Self {
26 Parameter::Symbol(SymbolicParameter::new(name))
27 }
28
29 pub fn symbol_with_value(name: &str, value: f64) -> Self {
31 Parameter::Symbol(SymbolicParameter::with_value(name, value))
32 }
33
34 pub fn value(&self) -> Option<f64> {
36 match self {
37 Parameter::Constant(val) => Some(*val),
38 Parameter::Symbol(sym) => sym.value,
39 }
40 }
41
42 pub fn has_value(&self) -> bool {
44 match self {
45 Parameter::Constant(_) => true,
46 Parameter::Symbol(sym) => sym.value.is_some(),
47 }
48 }
49}
50
51impl From<f64> for Parameter {
52 fn from(value: f64) -> Self {
53 Parameter::Constant(value)
54 }
55}
56
57impl Add for Parameter {
58 type Output = Self;
59
60 fn add(self, rhs: Self) -> Self::Output {
61 match (self.clone(), rhs.clone()) {
62 (Parameter::Constant(a), Parameter::Constant(b)) => Parameter::Constant(a + b),
63 _ => {
65 if let (Some(a_val), Some(b_val)) = (self.value(), rhs.value()) {
66 Parameter::Constant(a_val + b_val)
67 } else {
68 self
70 }
71 }
72 }
73 }
74}
75
76impl Sub for Parameter {
77 type Output = Self;
78
79 fn sub(self, rhs: Self) -> Self::Output {
80 match (self.clone(), rhs.clone()) {
81 (Parameter::Constant(a), Parameter::Constant(b)) => Parameter::Constant(a - b),
82 _ => {
84 if let (Some(a_val), Some(b_val)) = (self.value(), rhs.value()) {
85 Parameter::Constant(a_val - b_val)
86 } else {
87 self
89 }
90 }
91 }
92 }
93}
94
95impl Mul for Parameter {
96 type Output = Self;
97
98 fn mul(self, rhs: Self) -> Self::Output {
99 match (self.clone(), rhs.clone()) {
100 (Parameter::Constant(a), Parameter::Constant(b)) => Parameter::Constant(a * b),
101 _ => {
103 if let (Some(a_val), Some(b_val)) = (self.value(), rhs.value()) {
104 Parameter::Constant(a_val * b_val)
105 } else {
106 self
108 }
109 }
110 }
111 }
112}
113
114#[derive(Debug, Clone)]
116pub struct SymbolicParameter {
117 pub name: String,
119
120 pub value: Option<f64>,
122}
123
124impl SymbolicParameter {
125 pub fn new(name: &str) -> Self {
127 SymbolicParameter {
128 name: name.to_string(),
129 value: None,
130 }
131 }
132
133 pub fn with_value(name: &str, value: f64) -> Self {
135 SymbolicParameter {
136 name: name.to_string(),
137 value: Some(value),
138 }
139 }
140
141 pub fn set_value(&mut self, value: f64) {
143 self.value = Some(value);
144 }
145
146 pub fn clear_value(&mut self) {
148 self.value = None;
149 }
150}
151
152pub trait ParametricGate: GateOp {
157 fn parameters(&self) -> Vec<Parameter>;
159
160 fn parameter_names(&self) -> Vec<String>;
162
163 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>>;
165
166 fn with_parameter_at(
168 &self,
169 index: usize,
170 param: Parameter,
171 ) -> QuantRS2Result<Box<dyn ParametricGate>>;
172
173 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
175
176 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
178}
179
180#[derive(Debug, Clone)]
182pub struct ParametricRotationX {
183 pub target: QubitId,
185
186 pub theta: Parameter,
188}
189
190impl ParametricRotationX {
191 pub fn new(target: QubitId, theta: f64) -> Self {
193 ParametricRotationX {
194 target,
195 theta: Parameter::constant(theta),
196 }
197 }
198
199 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
201 ParametricRotationX {
202 target,
203 theta: Parameter::symbol(name),
204 }
205 }
206}
207
208impl GateOp for ParametricRotationX {
209 fn name(&self) -> &'static str {
210 "RX"
211 }
212
213 fn qubits(&self) -> Vec<QubitId> {
214 vec![self.target]
215 }
216
217 fn is_parameterized(&self) -> bool {
218 true
219 }
220
221 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
222 if let Some(theta) = self.theta.value() {
223 let cos = (theta / 2.0).cos();
224 let sin = (theta / 2.0).sin();
225 Ok(vec![
226 Complex64::new(cos, 0.0),
227 Complex64::new(0.0, -sin),
228 Complex64::new(0.0, -sin),
229 Complex64::new(cos, 0.0),
230 ])
231 } else {
232 Err(crate::error::QuantRS2Error::UnsupportedOperation(
233 "Cannot generate matrix for RX gate with unbound symbolic parameter".into(),
234 ))
235 }
236 }
237
238 fn as_any(&self) -> &dyn std::any::Any {
239 self
240 }
241}
242
243impl ParametricGate for ParametricRotationX {
244 fn parameters(&self) -> Vec<Parameter> {
245 vec![self.theta.clone()]
246 }
247
248 fn parameter_names(&self) -> Vec<String> {
249 match self.theta {
250 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
251 _ => Vec::new(),
252 }
253 }
254
255 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
256 if params.len() != 1 {
257 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
258 "RotationX expects 1 parameter, got {}",
259 params.len()
260 )));
261 }
262 Ok(Box::new(ParametricRotationX {
263 target: self.target,
264 theta: params[0].clone(),
265 }))
266 }
267
268 fn with_parameter_at(
269 &self,
270 index: usize,
271 param: Parameter,
272 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
273 if index != 0 {
274 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
275 "RotationX has only 1 parameter, got index {}",
276 index
277 )));
278 }
279 Ok(Box::new(ParametricRotationX {
280 target: self.target,
281 theta: param.clone(),
282 }))
283 }
284
285 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
286 match self.theta {
287 Parameter::Symbol(ref sym) => {
288 for (name, value) in values {
289 if sym.name == *name {
290 return Ok(Box::new(ParametricRotationX {
291 target: self.target,
292 theta: Parameter::Symbol(SymbolicParameter::with_value(
293 &sym.name, *value,
294 )),
295 }));
296 }
297 }
298 Ok(Box::new(self.clone()))
300 }
301 _ => Ok(Box::new(self.clone())), }
303 }
304
305 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
306 self.assign(values)
307 }
308}
309
310#[derive(Debug, Clone)]
312pub struct ParametricRotationY {
313 pub target: QubitId,
315
316 pub theta: Parameter,
318}
319
320impl ParametricRotationY {
321 pub fn new(target: QubitId, theta: f64) -> Self {
323 ParametricRotationY {
324 target,
325 theta: Parameter::constant(theta),
326 }
327 }
328
329 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
331 ParametricRotationY {
332 target,
333 theta: Parameter::symbol(name),
334 }
335 }
336}
337
338impl GateOp for ParametricRotationY {
339 fn name(&self) -> &'static str {
340 "RY"
341 }
342
343 fn qubits(&self) -> Vec<QubitId> {
344 vec![self.target]
345 }
346
347 fn is_parameterized(&self) -> bool {
348 true
349 }
350
351 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
352 if let Some(theta) = self.theta.value() {
353 let cos = (theta / 2.0).cos();
354 let sin = (theta / 2.0).sin();
355 Ok(vec![
356 Complex64::new(cos, 0.0),
357 Complex64::new(-sin, 0.0),
358 Complex64::new(sin, 0.0),
359 Complex64::new(cos, 0.0),
360 ])
361 } else {
362 Err(crate::error::QuantRS2Error::UnsupportedOperation(
363 "Cannot generate matrix for RY gate with unbound symbolic parameter".into(),
364 ))
365 }
366 }
367
368 fn as_any(&self) -> &dyn std::any::Any {
369 self
370 }
371}
372
373impl ParametricGate for ParametricRotationY {
374 fn parameters(&self) -> Vec<Parameter> {
375 vec![self.theta.clone()]
376 }
377
378 fn parameter_names(&self) -> Vec<String> {
379 match self.theta {
380 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
381 _ => Vec::new(),
382 }
383 }
384
385 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
386 if params.len() != 1 {
387 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
388 "RotationY expects 1 parameter, got {}",
389 params.len()
390 )));
391 }
392 Ok(Box::new(ParametricRotationY {
393 target: self.target,
394 theta: params[0].clone(),
395 }))
396 }
397
398 fn with_parameter_at(
399 &self,
400 index: usize,
401 param: Parameter,
402 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
403 if index != 0 {
404 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
405 "RotationY has only 1 parameter, got index {}",
406 index
407 )));
408 }
409 Ok(Box::new(ParametricRotationY {
410 target: self.target,
411 theta: param.clone(),
412 }))
413 }
414
415 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
416 match self.theta {
417 Parameter::Symbol(ref sym) => {
418 for (name, value) in values {
419 if sym.name == *name {
420 return Ok(Box::new(ParametricRotationY {
421 target: self.target,
422 theta: Parameter::Symbol(SymbolicParameter::with_value(
423 &sym.name, *value,
424 )),
425 }));
426 }
427 }
428 Ok(Box::new(self.clone()))
430 }
431 _ => Ok(Box::new(self.clone())), }
433 }
434
435 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
436 self.assign(values)
437 }
438}
439
440#[derive(Debug, Clone)]
442pub struct ParametricRotationZ {
443 pub target: QubitId,
445
446 pub theta: Parameter,
448}
449
450impl ParametricRotationZ {
451 pub fn new(target: QubitId, theta: f64) -> Self {
453 ParametricRotationZ {
454 target,
455 theta: Parameter::constant(theta),
456 }
457 }
458
459 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
461 ParametricRotationZ {
462 target,
463 theta: Parameter::symbol(name),
464 }
465 }
466}
467
468impl GateOp for ParametricRotationZ {
469 fn name(&self) -> &'static str {
470 "RZ"
471 }
472
473 fn qubits(&self) -> Vec<QubitId> {
474 vec![self.target]
475 }
476
477 fn is_parameterized(&self) -> bool {
478 true
479 }
480
481 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
482 if let Some(theta) = self.theta.value() {
483 let phase = Complex64::new(0.0, -theta / 2.0).exp();
484 let phase_conj = Complex64::new(0.0, theta / 2.0).exp();
485 Ok(vec![
486 phase_conj,
487 Complex64::new(0.0, 0.0),
488 Complex64::new(0.0, 0.0),
489 phase,
490 ])
491 } else {
492 Err(crate::error::QuantRS2Error::UnsupportedOperation(
493 "Cannot generate matrix for RZ gate with unbound symbolic parameter".into(),
494 ))
495 }
496 }
497
498 fn as_any(&self) -> &dyn std::any::Any {
499 self
500 }
501}
502
503impl ParametricGate for ParametricRotationZ {
504 fn parameters(&self) -> Vec<Parameter> {
505 vec![self.theta.clone()]
506 }
507
508 fn parameter_names(&self) -> Vec<String> {
509 match self.theta {
510 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
511 _ => Vec::new(),
512 }
513 }
514
515 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
516 if params.len() != 1 {
517 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
518 "RotationZ expects 1 parameter, got {}",
519 params.len()
520 )));
521 }
522 Ok(Box::new(ParametricRotationZ {
523 target: self.target,
524 theta: params[0].clone(),
525 }))
526 }
527
528 fn with_parameter_at(
529 &self,
530 index: usize,
531 param: Parameter,
532 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
533 if index != 0 {
534 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
535 "RotationZ has only 1 parameter, got index {}",
536 index
537 )));
538 }
539 Ok(Box::new(ParametricRotationZ {
540 target: self.target,
541 theta: param.clone(),
542 }))
543 }
544
545 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
546 match self.theta {
547 Parameter::Symbol(ref sym) => {
548 for (name, value) in values {
549 if sym.name == *name {
550 return Ok(Box::new(ParametricRotationZ {
551 target: self.target,
552 theta: Parameter::Symbol(SymbolicParameter::with_value(
553 &sym.name, *value,
554 )),
555 }));
556 }
557 }
558 Ok(Box::new(self.clone()))
560 }
561 _ => Ok(Box::new(self.clone())), }
563 }
564
565 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
566 self.assign(values)
567 }
568}
569
570#[derive(Debug, Clone)]
572pub struct ParametricU {
573 pub target: QubitId,
575
576 pub theta: Parameter,
578
579 pub phi: Parameter,
581
582 pub lambda: Parameter,
584}
585
586impl ParametricU {
587 pub fn new(target: QubitId, theta: f64, phi: f64, lambda: f64) -> Self {
589 ParametricU {
590 target,
591 theta: Parameter::constant(theta),
592 phi: Parameter::constant(phi),
593 lambda: Parameter::constant(lambda),
594 }
595 }
596
597 pub fn new_symbolic(
599 target: QubitId,
600 theta_name: &str,
601 phi_name: &str,
602 lambda_name: &str,
603 ) -> Self {
604 ParametricU {
605 target,
606 theta: Parameter::symbol(theta_name),
607 phi: Parameter::symbol(phi_name),
608 lambda: Parameter::symbol(lambda_name),
609 }
610 }
611}
612
613impl GateOp for ParametricU {
614 fn name(&self) -> &'static str {
615 "U"
616 }
617
618 fn qubits(&self) -> Vec<QubitId> {
619 vec![self.target]
620 }
621
622 fn is_parameterized(&self) -> bool {
623 true
624 }
625
626 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
627 if let (Some(theta), Some(phi), Some(lambda)) =
628 (self.theta.value(), self.phi.value(), self.lambda.value())
629 {
630 let cos = (theta / 2.0).cos();
631 let sin = (theta / 2.0).sin();
632
633 let e_phi = Complex64::new(0.0, phi).exp();
634 let e_lambda = Complex64::new(0.0, lambda).exp();
635
636 Ok(vec![
637 Complex64::new(cos, 0.0),
638 -sin * e_lambda,
639 sin * e_phi,
640 cos * e_phi * e_lambda,
641 ])
642 } else {
643 Err(crate::error::QuantRS2Error::UnsupportedOperation(
644 "Cannot generate matrix for U gate with unbound symbolic parameters".into(),
645 ))
646 }
647 }
648
649 fn as_any(&self) -> &dyn std::any::Any {
650 self
651 }
652}
653
654impl ParametricGate for ParametricU {
655 fn parameters(&self) -> Vec<Parameter> {
656 vec![self.theta.clone(), self.phi.clone(), self.lambda.clone()]
657 }
658
659 fn parameter_names(&self) -> Vec<String> {
660 let mut names = Vec::new();
661
662 if let Parameter::Symbol(ref sym) = self.theta {
663 names.push(sym.name.clone());
664 }
665
666 if let Parameter::Symbol(ref sym) = self.phi {
667 names.push(sym.name.clone());
668 }
669
670 if let Parameter::Symbol(ref sym) = self.lambda {
671 names.push(sym.name.clone());
672 }
673
674 names
675 }
676
677 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
678 if params.len() != 3 {
679 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
680 "U gate expects 3 parameters, got {}",
681 params.len()
682 )));
683 }
684 Ok(Box::new(ParametricU {
685 target: self.target,
686 theta: params[0].clone(),
687 phi: params[1].clone(),
688 lambda: params[2].clone(),
689 }))
690 }
691
692 fn with_parameter_at(
693 &self,
694 index: usize,
695 param: Parameter,
696 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
697 match index {
698 0 => Ok(Box::new(ParametricU {
699 target: self.target,
700 theta: param.clone(),
701 phi: self.phi.clone(),
702 lambda: self.lambda.clone(),
703 })),
704 1 => Ok(Box::new(ParametricU {
705 target: self.target,
706 theta: self.theta.clone(),
707 phi: param.clone(),
708 lambda: self.lambda.clone(),
709 })),
710 2 => Ok(Box::new(ParametricU {
711 target: self.target,
712 theta: self.theta.clone(),
713 phi: self.phi.clone(),
714 lambda: param.clone(),
715 })),
716 _ => Err(crate::error::QuantRS2Error::InvalidInput(format!(
717 "U gate has only 3 parameters, got index {}",
718 index
719 ))),
720 }
721 }
722
723 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
724 let mut result = self.clone();
725
726 if let Parameter::Symbol(ref sym) = self.theta {
728 for (name, value) in values {
729 if sym.name == *name {
730 result.theta =
731 Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
732 break;
733 }
734 }
735 }
736
737 if let Parameter::Symbol(ref sym) = self.phi {
739 for (name, value) in values {
740 if sym.name == *name {
741 result.phi =
742 Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
743 break;
744 }
745 }
746 }
747
748 if let Parameter::Symbol(ref sym) = self.lambda {
750 for (name, value) in values {
751 if sym.name == *name {
752 result.lambda =
753 Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
754 break;
755 }
756 }
757 }
758
759 Ok(Box::new(result))
760 }
761
762 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
763 self.assign(values)
764 }
765}
766
767#[derive(Debug, Clone)]
769pub struct ParametricCRX {
770 pub control: QubitId,
772
773 pub target: QubitId,
775
776 pub theta: Parameter,
778}
779
780impl ParametricCRX {
781 pub fn new(control: QubitId, target: QubitId, theta: f64) -> Self {
783 ParametricCRX {
784 control,
785 target,
786 theta: Parameter::constant(theta),
787 }
788 }
789
790 pub fn new_symbolic(control: QubitId, target: QubitId, name: &str) -> Self {
792 ParametricCRX {
793 control,
794 target,
795 theta: Parameter::symbol(name),
796 }
797 }
798}
799
800impl GateOp for ParametricCRX {
801 fn name(&self) -> &'static str {
802 "CRX"
803 }
804
805 fn qubits(&self) -> Vec<QubitId> {
806 vec![self.control, self.target]
807 }
808
809 fn is_parameterized(&self) -> bool {
810 true
811 }
812
813 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
814 if let Some(theta) = self.theta.value() {
815 let cos = (theta / 2.0).cos();
816 let sin = (theta / 2.0).sin();
817
818 Ok(vec![
819 Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(cos, 0.0), Complex64::new(0.0, -sin), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, -sin), Complex64::new(cos, 0.0), ])
836 } else {
837 Err(crate::error::QuantRS2Error::UnsupportedOperation(
838 "Cannot generate matrix for CRX gate with unbound symbolic parameter".into(),
839 ))
840 }
841 }
842
843 fn as_any(&self) -> &dyn std::any::Any {
844 self
845 }
846}
847
848impl ParametricGate for ParametricCRX {
849 fn parameters(&self) -> Vec<Parameter> {
850 vec![self.theta.clone()]
851 }
852
853 fn parameter_names(&self) -> Vec<String> {
854 match self.theta {
855 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
856 _ => Vec::new(),
857 }
858 }
859
860 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
861 if params.len() != 1 {
862 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
863 "CRX expects 1 parameter, got {}",
864 params.len()
865 )));
866 }
867 Ok(Box::new(ParametricCRX {
868 control: self.control,
869 target: self.target,
870 theta: params[0].clone(),
871 }))
872 }
873
874 fn with_parameter_at(
875 &self,
876 index: usize,
877 param: Parameter,
878 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
879 if index != 0 {
880 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
881 "CRX has only 1 parameter, got index {}",
882 index
883 )));
884 }
885 Ok(Box::new(ParametricCRX {
886 control: self.control,
887 target: self.target,
888 theta: param.clone(),
889 }))
890 }
891
892 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
893 match self.theta {
894 Parameter::Symbol(ref sym) => {
895 for (name, value) in values {
896 if sym.name == *name {
897 return Ok(Box::new(ParametricCRX {
898 control: self.control,
899 target: self.target,
900 theta: Parameter::Symbol(SymbolicParameter::with_value(
901 &sym.name, *value,
902 )),
903 }));
904 }
905 }
906 Ok(Box::new(self.clone()))
908 }
909 _ => Ok(Box::new(self.clone())), }
911 }
912
913 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
914 self.assign(values)
915 }
916}
917
918#[derive(Debug, Clone)]
920pub struct ParametricPhaseShift {
921 pub target: QubitId,
923
924 pub phi: Parameter,
926}
927
928impl ParametricPhaseShift {
929 pub fn new(target: QubitId, phi: f64) -> Self {
931 ParametricPhaseShift {
932 target,
933 phi: Parameter::constant(phi),
934 }
935 }
936
937 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
939 ParametricPhaseShift {
940 target,
941 phi: Parameter::symbol(name),
942 }
943 }
944}
945
946impl GateOp for ParametricPhaseShift {
947 fn name(&self) -> &'static str {
948 "P"
949 }
950
951 fn qubits(&self) -> Vec<QubitId> {
952 vec![self.target]
953 }
954
955 fn is_parameterized(&self) -> bool {
956 true
957 }
958
959 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
960 if let Some(phi) = self.phi.value() {
961 let phase = Complex64::new(phi.cos(), phi.sin());
962 Ok(vec![
963 Complex64::new(1.0, 0.0),
964 Complex64::new(0.0, 0.0),
965 Complex64::new(0.0, 0.0),
966 phase,
967 ])
968 } else {
969 Err(crate::error::QuantRS2Error::UnsupportedOperation(
970 "Cannot generate matrix for phase shift gate with unbound symbolic parameter"
971 .into(),
972 ))
973 }
974 }
975
976 fn as_any(&self) -> &dyn std::any::Any {
977 self
978 }
979}
980
981impl ParametricGate for ParametricPhaseShift {
982 fn parameters(&self) -> Vec<Parameter> {
983 vec![self.phi.clone()]
984 }
985
986 fn parameter_names(&self) -> Vec<String> {
987 match self.phi {
988 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
989 _ => Vec::new(),
990 }
991 }
992
993 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
994 if params.len() != 1 {
995 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
996 "Phase shift gate expects 1 parameter, got {}",
997 params.len()
998 )));
999 }
1000 Ok(Box::new(ParametricPhaseShift {
1001 target: self.target,
1002 phi: params[0].clone(),
1003 }))
1004 }
1005
1006 fn with_parameter_at(
1007 &self,
1008 index: usize,
1009 param: Parameter,
1010 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
1011 if index != 0 {
1012 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
1013 "Phase shift gate has only 1 parameter, got index {}",
1014 index
1015 )));
1016 }
1017 Ok(Box::new(ParametricPhaseShift {
1018 target: self.target,
1019 phi: param.clone(),
1020 }))
1021 }
1022
1023 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1024 match self.phi {
1025 Parameter::Symbol(ref sym) => {
1026 for (name, value) in values {
1027 if sym.name == *name {
1028 return Ok(Box::new(ParametricPhaseShift {
1029 target: self.target,
1030 phi: Parameter::Symbol(SymbolicParameter::with_value(
1031 &sym.name, *value,
1032 )),
1033 }));
1034 }
1035 }
1036 Ok(Box::new(self.clone()))
1038 }
1039 _ => Ok(Box::new(self.clone())), }
1041 }
1042
1043 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1044 self.assign(values)
1045 }
1046}
1047
1048pub mod utils {
1050 use super::*;
1051 use crate::gate::{multi, single};
1052
1053 pub fn parametrize_rotation_gate(gate: &dyn GateOp) -> Option<Box<dyn ParametricGate>> {
1055 if !gate.is_parameterized() {
1056 return None;
1057 }
1058
1059 if let Some(rx) = gate.as_any().downcast_ref::<single::RotationX>() {
1060 Some(Box::new(ParametricRotationX::new(rx.target, rx.theta)))
1061 } else if let Some(ry) = gate.as_any().downcast_ref::<single::RotationY>() {
1062 Some(Box::new(ParametricRotationY::new(ry.target, ry.theta)))
1063 } else if let Some(rz) = gate.as_any().downcast_ref::<single::RotationZ>() {
1064 Some(Box::new(ParametricRotationZ::new(rz.target, rz.theta)))
1065 } else if let Some(crx) = gate.as_any().downcast_ref::<multi::CRX>() {
1066 Some(Box::new(ParametricCRX::new(
1067 crx.control,
1068 crx.target,
1069 crx.theta,
1070 )))
1071 } else {
1072 None
1073 }
1074 }
1075
1076 pub fn symbolize_parameter(param: f64, name: &str) -> Parameter {
1078 Parameter::symbol_with_value(name, param)
1079 }
1080
1081 pub fn parameters_approx_eq(p1: &Parameter, p2: &Parameter, epsilon: f64) -> bool {
1083 match (p1.value(), p2.value()) {
1084 (Some(v1), Some(v2)) => (v1 - v2).abs() < epsilon,
1085 _ => false,
1086 }
1087 }
1088}