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 fn clone_gate(&self) -> Box<dyn GateOp> {
243 Box::new(self.clone())
244 }
245}
246
247impl ParametricGate for ParametricRotationX {
248 fn parameters(&self) -> Vec<Parameter> {
249 vec![self.theta.clone()]
250 }
251
252 fn parameter_names(&self) -> Vec<String> {
253 match self.theta {
254 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
255 _ => Vec::new(),
256 }
257 }
258
259 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
260 if params.len() != 1 {
261 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
262 "RotationX expects 1 parameter, got {}",
263 params.len()
264 )));
265 }
266 Ok(Box::new(ParametricRotationX {
267 target: self.target,
268 theta: params[0].clone(),
269 }))
270 }
271
272 fn with_parameter_at(
273 &self,
274 index: usize,
275 param: Parameter,
276 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
277 if index != 0 {
278 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
279 "RotationX has only 1 parameter, got index {}",
280 index
281 )));
282 }
283 Ok(Box::new(ParametricRotationX {
284 target: self.target,
285 theta: param.clone(),
286 }))
287 }
288
289 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
290 match self.theta {
291 Parameter::Symbol(ref sym) => {
292 for (name, value) in values {
293 if sym.name == *name {
294 return Ok(Box::new(ParametricRotationX {
295 target: self.target,
296 theta: Parameter::Symbol(SymbolicParameter::with_value(
297 &sym.name, *value,
298 )),
299 }));
300 }
301 }
302 Ok(Box::new(self.clone()))
304 }
305 _ => Ok(Box::new(self.clone())), }
307 }
308
309 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
310 self.assign(values)
311 }
312}
313
314#[derive(Debug, Clone)]
316pub struct ParametricRotationY {
317 pub target: QubitId,
319
320 pub theta: Parameter,
322}
323
324impl ParametricRotationY {
325 pub fn new(target: QubitId, theta: f64) -> Self {
327 ParametricRotationY {
328 target,
329 theta: Parameter::constant(theta),
330 }
331 }
332
333 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
335 ParametricRotationY {
336 target,
337 theta: Parameter::symbol(name),
338 }
339 }
340}
341
342impl GateOp for ParametricRotationY {
343 fn name(&self) -> &'static str {
344 "RY"
345 }
346
347 fn qubits(&self) -> Vec<QubitId> {
348 vec![self.target]
349 }
350
351 fn is_parameterized(&self) -> bool {
352 true
353 }
354
355 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
356 if let Some(theta) = self.theta.value() {
357 let cos = (theta / 2.0).cos();
358 let sin = (theta / 2.0).sin();
359 Ok(vec![
360 Complex64::new(cos, 0.0),
361 Complex64::new(-sin, 0.0),
362 Complex64::new(sin, 0.0),
363 Complex64::new(cos, 0.0),
364 ])
365 } else {
366 Err(crate::error::QuantRS2Error::UnsupportedOperation(
367 "Cannot generate matrix for RY gate with unbound symbolic parameter".into(),
368 ))
369 }
370 }
371
372 fn as_any(&self) -> &dyn std::any::Any {
373 self
374 }
375
376 fn clone_gate(&self) -> Box<dyn GateOp> {
377 Box::new(self.clone())
378 }
379}
380
381impl ParametricGate for ParametricRotationY {
382 fn parameters(&self) -> Vec<Parameter> {
383 vec![self.theta.clone()]
384 }
385
386 fn parameter_names(&self) -> Vec<String> {
387 match self.theta {
388 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
389 _ => Vec::new(),
390 }
391 }
392
393 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
394 if params.len() != 1 {
395 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
396 "RotationY expects 1 parameter, got {}",
397 params.len()
398 )));
399 }
400 Ok(Box::new(ParametricRotationY {
401 target: self.target,
402 theta: params[0].clone(),
403 }))
404 }
405
406 fn with_parameter_at(
407 &self,
408 index: usize,
409 param: Parameter,
410 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
411 if index != 0 {
412 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
413 "RotationY has only 1 parameter, got index {}",
414 index
415 )));
416 }
417 Ok(Box::new(ParametricRotationY {
418 target: self.target,
419 theta: param.clone(),
420 }))
421 }
422
423 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
424 match self.theta {
425 Parameter::Symbol(ref sym) => {
426 for (name, value) in values {
427 if sym.name == *name {
428 return Ok(Box::new(ParametricRotationY {
429 target: self.target,
430 theta: Parameter::Symbol(SymbolicParameter::with_value(
431 &sym.name, *value,
432 )),
433 }));
434 }
435 }
436 Ok(Box::new(self.clone()))
438 }
439 _ => Ok(Box::new(self.clone())), }
441 }
442
443 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
444 self.assign(values)
445 }
446}
447
448#[derive(Debug, Clone)]
450pub struct ParametricRotationZ {
451 pub target: QubitId,
453
454 pub theta: Parameter,
456}
457
458impl ParametricRotationZ {
459 pub fn new(target: QubitId, theta: f64) -> Self {
461 ParametricRotationZ {
462 target,
463 theta: Parameter::constant(theta),
464 }
465 }
466
467 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
469 ParametricRotationZ {
470 target,
471 theta: Parameter::symbol(name),
472 }
473 }
474}
475
476impl GateOp for ParametricRotationZ {
477 fn name(&self) -> &'static str {
478 "RZ"
479 }
480
481 fn qubits(&self) -> Vec<QubitId> {
482 vec![self.target]
483 }
484
485 fn is_parameterized(&self) -> bool {
486 true
487 }
488
489 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
490 if let Some(theta) = self.theta.value() {
491 let phase = Complex64::new(0.0, -theta / 2.0).exp();
492 let phase_conj = Complex64::new(0.0, theta / 2.0).exp();
493 Ok(vec![
494 phase_conj,
495 Complex64::new(0.0, 0.0),
496 Complex64::new(0.0, 0.0),
497 phase,
498 ])
499 } else {
500 Err(crate::error::QuantRS2Error::UnsupportedOperation(
501 "Cannot generate matrix for RZ gate with unbound symbolic parameter".into(),
502 ))
503 }
504 }
505
506 fn as_any(&self) -> &dyn std::any::Any {
507 self
508 }
509
510 fn clone_gate(&self) -> Box<dyn GateOp> {
511 Box::new(self.clone())
512 }
513}
514
515impl ParametricGate for ParametricRotationZ {
516 fn parameters(&self) -> Vec<Parameter> {
517 vec![self.theta.clone()]
518 }
519
520 fn parameter_names(&self) -> Vec<String> {
521 match self.theta {
522 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
523 _ => Vec::new(),
524 }
525 }
526
527 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
528 if params.len() != 1 {
529 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
530 "RotationZ expects 1 parameter, got {}",
531 params.len()
532 )));
533 }
534 Ok(Box::new(ParametricRotationZ {
535 target: self.target,
536 theta: params[0].clone(),
537 }))
538 }
539
540 fn with_parameter_at(
541 &self,
542 index: usize,
543 param: Parameter,
544 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
545 if index != 0 {
546 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
547 "RotationZ has only 1 parameter, got index {}",
548 index
549 )));
550 }
551 Ok(Box::new(ParametricRotationZ {
552 target: self.target,
553 theta: param.clone(),
554 }))
555 }
556
557 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
558 match self.theta {
559 Parameter::Symbol(ref sym) => {
560 for (name, value) in values {
561 if sym.name == *name {
562 return Ok(Box::new(ParametricRotationZ {
563 target: self.target,
564 theta: Parameter::Symbol(SymbolicParameter::with_value(
565 &sym.name, *value,
566 )),
567 }));
568 }
569 }
570 Ok(Box::new(self.clone()))
572 }
573 _ => Ok(Box::new(self.clone())), }
575 }
576
577 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
578 self.assign(values)
579 }
580}
581
582#[derive(Debug, Clone)]
584pub struct ParametricU {
585 pub target: QubitId,
587
588 pub theta: Parameter,
590
591 pub phi: Parameter,
593
594 pub lambda: Parameter,
596}
597
598impl ParametricU {
599 pub fn new(target: QubitId, theta: f64, phi: f64, lambda: f64) -> Self {
601 ParametricU {
602 target,
603 theta: Parameter::constant(theta),
604 phi: Parameter::constant(phi),
605 lambda: Parameter::constant(lambda),
606 }
607 }
608
609 pub fn new_symbolic(
611 target: QubitId,
612 theta_name: &str,
613 phi_name: &str,
614 lambda_name: &str,
615 ) -> Self {
616 ParametricU {
617 target,
618 theta: Parameter::symbol(theta_name),
619 phi: Parameter::symbol(phi_name),
620 lambda: Parameter::symbol(lambda_name),
621 }
622 }
623}
624
625impl GateOp for ParametricU {
626 fn name(&self) -> &'static str {
627 "U"
628 }
629
630 fn qubits(&self) -> Vec<QubitId> {
631 vec![self.target]
632 }
633
634 fn is_parameterized(&self) -> bool {
635 true
636 }
637
638 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
639 if let (Some(theta), Some(phi), Some(lambda)) =
640 (self.theta.value(), self.phi.value(), self.lambda.value())
641 {
642 let cos = (theta / 2.0).cos();
643 let sin = (theta / 2.0).sin();
644
645 let e_phi = Complex64::new(0.0, phi).exp();
646 let e_lambda = Complex64::new(0.0, lambda).exp();
647
648 Ok(vec![
649 Complex64::new(cos, 0.0),
650 -sin * e_lambda,
651 sin * e_phi,
652 cos * e_phi * e_lambda,
653 ])
654 } else {
655 Err(crate::error::QuantRS2Error::UnsupportedOperation(
656 "Cannot generate matrix for U gate with unbound symbolic parameters".into(),
657 ))
658 }
659 }
660
661 fn as_any(&self) -> &dyn std::any::Any {
662 self
663 }
664
665 fn clone_gate(&self) -> Box<dyn GateOp> {
666 Box::new(self.clone())
667 }
668}
669
670impl ParametricGate for ParametricU {
671 fn parameters(&self) -> Vec<Parameter> {
672 vec![self.theta.clone(), self.phi.clone(), self.lambda.clone()]
673 }
674
675 fn parameter_names(&self) -> Vec<String> {
676 let mut names = Vec::new();
677
678 if let Parameter::Symbol(ref sym) = self.theta {
679 names.push(sym.name.clone());
680 }
681
682 if let Parameter::Symbol(ref sym) = self.phi {
683 names.push(sym.name.clone());
684 }
685
686 if let Parameter::Symbol(ref sym) = self.lambda {
687 names.push(sym.name.clone());
688 }
689
690 names
691 }
692
693 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
694 if params.len() != 3 {
695 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
696 "U gate expects 3 parameters, got {}",
697 params.len()
698 )));
699 }
700 Ok(Box::new(ParametricU {
701 target: self.target,
702 theta: params[0].clone(),
703 phi: params[1].clone(),
704 lambda: params[2].clone(),
705 }))
706 }
707
708 fn with_parameter_at(
709 &self,
710 index: usize,
711 param: Parameter,
712 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
713 match index {
714 0 => Ok(Box::new(ParametricU {
715 target: self.target,
716 theta: param.clone(),
717 phi: self.phi.clone(),
718 lambda: self.lambda.clone(),
719 })),
720 1 => Ok(Box::new(ParametricU {
721 target: self.target,
722 theta: self.theta.clone(),
723 phi: param.clone(),
724 lambda: self.lambda.clone(),
725 })),
726 2 => Ok(Box::new(ParametricU {
727 target: self.target,
728 theta: self.theta.clone(),
729 phi: self.phi.clone(),
730 lambda: param.clone(),
731 })),
732 _ => Err(crate::error::QuantRS2Error::InvalidInput(format!(
733 "U gate has only 3 parameters, got index {}",
734 index
735 ))),
736 }
737 }
738
739 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
740 let mut result = self.clone();
741
742 if let Parameter::Symbol(ref sym) = self.theta {
744 for (name, value) in values {
745 if sym.name == *name {
746 result.theta =
747 Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
748 break;
749 }
750 }
751 }
752
753 if let Parameter::Symbol(ref sym) = self.phi {
755 for (name, value) in values {
756 if sym.name == *name {
757 result.phi =
758 Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
759 break;
760 }
761 }
762 }
763
764 if let Parameter::Symbol(ref sym) = self.lambda {
766 for (name, value) in values {
767 if sym.name == *name {
768 result.lambda =
769 Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
770 break;
771 }
772 }
773 }
774
775 Ok(Box::new(result))
776 }
777
778 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
779 self.assign(values)
780 }
781}
782
783#[derive(Debug, Clone)]
785pub struct ParametricCRX {
786 pub control: QubitId,
788
789 pub target: QubitId,
791
792 pub theta: Parameter,
794}
795
796impl ParametricCRX {
797 pub fn new(control: QubitId, target: QubitId, theta: f64) -> Self {
799 ParametricCRX {
800 control,
801 target,
802 theta: Parameter::constant(theta),
803 }
804 }
805
806 pub fn new_symbolic(control: QubitId, target: QubitId, name: &str) -> Self {
808 ParametricCRX {
809 control,
810 target,
811 theta: Parameter::symbol(name),
812 }
813 }
814}
815
816impl GateOp for ParametricCRX {
817 fn name(&self) -> &'static str {
818 "CRX"
819 }
820
821 fn qubits(&self) -> Vec<QubitId> {
822 vec![self.control, self.target]
823 }
824
825 fn is_parameterized(&self) -> bool {
826 true
827 }
828
829 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
830 if let Some(theta) = self.theta.value() {
831 let cos = (theta / 2.0).cos();
832 let sin = (theta / 2.0).sin();
833
834 Ok(vec![
835 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), ])
852 } else {
853 Err(crate::error::QuantRS2Error::UnsupportedOperation(
854 "Cannot generate matrix for CRX gate with unbound symbolic parameter".into(),
855 ))
856 }
857 }
858
859 fn as_any(&self) -> &dyn std::any::Any {
860 self
861 }
862
863 fn clone_gate(&self) -> Box<dyn GateOp> {
864 Box::new(self.clone())
865 }
866}
867
868impl ParametricGate for ParametricCRX {
869 fn parameters(&self) -> Vec<Parameter> {
870 vec![self.theta.clone()]
871 }
872
873 fn parameter_names(&self) -> Vec<String> {
874 match self.theta {
875 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
876 _ => Vec::new(),
877 }
878 }
879
880 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
881 if params.len() != 1 {
882 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
883 "CRX expects 1 parameter, got {}",
884 params.len()
885 )));
886 }
887 Ok(Box::new(ParametricCRX {
888 control: self.control,
889 target: self.target,
890 theta: params[0].clone(),
891 }))
892 }
893
894 fn with_parameter_at(
895 &self,
896 index: usize,
897 param: Parameter,
898 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
899 if index != 0 {
900 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
901 "CRX has only 1 parameter, got index {}",
902 index
903 )));
904 }
905 Ok(Box::new(ParametricCRX {
906 control: self.control,
907 target: self.target,
908 theta: param.clone(),
909 }))
910 }
911
912 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
913 match self.theta {
914 Parameter::Symbol(ref sym) => {
915 for (name, value) in values {
916 if sym.name == *name {
917 return Ok(Box::new(ParametricCRX {
918 control: self.control,
919 target: self.target,
920 theta: Parameter::Symbol(SymbolicParameter::with_value(
921 &sym.name, *value,
922 )),
923 }));
924 }
925 }
926 Ok(Box::new(self.clone()))
928 }
929 _ => Ok(Box::new(self.clone())), }
931 }
932
933 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
934 self.assign(values)
935 }
936}
937
938#[derive(Debug, Clone)]
940pub struct ParametricPhaseShift {
941 pub target: QubitId,
943
944 pub phi: Parameter,
946}
947
948impl ParametricPhaseShift {
949 pub fn new(target: QubitId, phi: f64) -> Self {
951 ParametricPhaseShift {
952 target,
953 phi: Parameter::constant(phi),
954 }
955 }
956
957 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
959 ParametricPhaseShift {
960 target,
961 phi: Parameter::symbol(name),
962 }
963 }
964}
965
966impl GateOp for ParametricPhaseShift {
967 fn name(&self) -> &'static str {
968 "P"
969 }
970
971 fn qubits(&self) -> Vec<QubitId> {
972 vec![self.target]
973 }
974
975 fn is_parameterized(&self) -> bool {
976 true
977 }
978
979 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
980 if let Some(phi) = self.phi.value() {
981 let phase = Complex64::new(phi.cos(), phi.sin());
982 Ok(vec![
983 Complex64::new(1.0, 0.0),
984 Complex64::new(0.0, 0.0),
985 Complex64::new(0.0, 0.0),
986 phase,
987 ])
988 } else {
989 Err(crate::error::QuantRS2Error::UnsupportedOperation(
990 "Cannot generate matrix for phase shift gate with unbound symbolic parameter"
991 .into(),
992 ))
993 }
994 }
995
996 fn as_any(&self) -> &dyn std::any::Any {
997 self
998 }
999
1000 fn clone_gate(&self) -> Box<dyn GateOp> {
1001 Box::new(self.clone())
1002 }
1003}
1004
1005impl ParametricGate for ParametricPhaseShift {
1006 fn parameters(&self) -> Vec<Parameter> {
1007 vec![self.phi.clone()]
1008 }
1009
1010 fn parameter_names(&self) -> Vec<String> {
1011 match self.phi {
1012 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
1013 _ => Vec::new(),
1014 }
1015 }
1016
1017 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1018 if params.len() != 1 {
1019 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
1020 "Phase shift gate expects 1 parameter, got {}",
1021 params.len()
1022 )));
1023 }
1024 Ok(Box::new(ParametricPhaseShift {
1025 target: self.target,
1026 phi: params[0].clone(),
1027 }))
1028 }
1029
1030 fn with_parameter_at(
1031 &self,
1032 index: usize,
1033 param: Parameter,
1034 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
1035 if index != 0 {
1036 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
1037 "Phase shift gate has only 1 parameter, got index {}",
1038 index
1039 )));
1040 }
1041 Ok(Box::new(ParametricPhaseShift {
1042 target: self.target,
1043 phi: param.clone(),
1044 }))
1045 }
1046
1047 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1048 match self.phi {
1049 Parameter::Symbol(ref sym) => {
1050 for (name, value) in values {
1051 if sym.name == *name {
1052 return Ok(Box::new(ParametricPhaseShift {
1053 target: self.target,
1054 phi: Parameter::Symbol(SymbolicParameter::with_value(
1055 &sym.name, *value,
1056 )),
1057 }));
1058 }
1059 }
1060 Ok(Box::new(self.clone()))
1062 }
1063 _ => Ok(Box::new(self.clone())), }
1065 }
1066
1067 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1068 self.assign(values)
1069 }
1070}
1071
1072pub mod utils {
1074 use super::*;
1075 use crate::gate::{multi, single};
1076
1077 pub fn parametrize_rotation_gate(gate: &dyn GateOp) -> Option<Box<dyn ParametricGate>> {
1079 if !gate.is_parameterized() {
1080 return None;
1081 }
1082
1083 if let Some(rx) = gate.as_any().downcast_ref::<single::RotationX>() {
1084 Some(Box::new(ParametricRotationX::new(rx.target, rx.theta)))
1085 } else if let Some(ry) = gate.as_any().downcast_ref::<single::RotationY>() {
1086 Some(Box::new(ParametricRotationY::new(ry.target, ry.theta)))
1087 } else if let Some(rz) = gate.as_any().downcast_ref::<single::RotationZ>() {
1088 Some(Box::new(ParametricRotationZ::new(rz.target, rz.theta)))
1089 } else if let Some(crx) = gate.as_any().downcast_ref::<multi::CRX>() {
1090 Some(Box::new(ParametricCRX::new(
1091 crx.control,
1092 crx.target,
1093 crx.theta,
1094 )))
1095 } else {
1096 None
1097 }
1098 }
1099
1100 pub fn symbolize_parameter(param: f64, name: &str) -> Parameter {
1102 Parameter::symbol_with_value(name, param)
1103 }
1104
1105 pub fn parameters_approx_eq(p1: &Parameter, p2: &Parameter, epsilon: f64) -> bool {
1107 match (p1.value(), p2.value()) {
1108 (Some(v1), Some(v2)) => (v1 - v2).abs() < epsilon,
1109 _ => false,
1110 }
1111 }
1112}