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