1use scirs2_core::Complex64;
8use std::collections::HashMap;
9use std::ops::{Add, Div, Mul, Sub};
10
11use crate::error::QuantRS2Result;
12use crate::gate::GateOp;
13use crate::qubit::QubitId;
14use crate::symbolic::SymbolicExpression;
15
16#[derive(Debug, Clone)]
18pub enum Parameter {
19 Constant(f64),
21
22 ComplexConstant(Complex64),
24
25 Symbol(SymbolicParameter),
27
28 Symbolic(SymbolicExpression),
30}
31
32impl Parameter {
33 pub const fn constant(value: f64) -> Self {
35 Self::Constant(value)
36 }
37
38 pub const fn complex_constant(value: Complex64) -> Self {
40 Self::ComplexConstant(value)
41 }
42
43 pub fn symbol(name: &str) -> Self {
45 Self::Symbol(SymbolicParameter::new(name))
46 }
47
48 pub fn symbol_with_value(name: &str, value: f64) -> Self {
50 Self::Symbol(SymbolicParameter::with_value(name, value))
51 }
52
53 pub const fn symbolic(expr: SymbolicExpression) -> Self {
55 Self::Symbolic(expr)
56 }
57
58 pub fn variable(name: &str) -> Self {
60 Self::Symbolic(SymbolicExpression::variable(name))
61 }
62
63 pub fn parse(expr: &str) -> QuantRS2Result<Self> {
65 if let Ok(value) = expr.parse::<f64>() {
67 return Ok(Self::Constant(value));
68 }
69
70 let symbolic_expr = SymbolicExpression::parse(expr)?;
72 Ok(Self::Symbolic(symbolic_expr))
73 }
74
75 pub fn value(&self) -> Option<f64> {
77 match self {
78 Self::Constant(val) => Some(*val),
79 Self::ComplexConstant(val) => {
80 if val.im.abs() < 1e-12 {
81 Some(val.re)
82 } else {
83 None }
85 }
86 Self::Symbol(sym) => sym.value,
87 Self::Symbolic(expr) => {
88 expr.evaluate(&HashMap::new()).ok()
90 }
91 }
92 }
93
94 pub fn complex_value(&self) -> Option<Complex64> {
96 match self {
97 Self::Constant(val) => Some(Complex64::new(*val, 0.0)),
98 Self::ComplexConstant(val) => Some(*val),
99 Self::Symbol(sym) => sym.value.map(|v| Complex64::new(v, 0.0)),
100 Self::Symbolic(expr) => {
101 expr.evaluate_complex(&HashMap::new()).ok()
103 }
104 }
105 }
106
107 pub fn has_value(&self) -> bool {
109 match self {
110 Self::Constant(_) | Self::ComplexConstant(_) => true,
111 Self::Symbol(sym) => sym.value.is_some(),
112 Self::Symbolic(expr) => expr.is_constant(),
113 }
114 }
115
116 pub fn evaluate(&self, variables: &HashMap<String, f64>) -> QuantRS2Result<f64> {
118 match self {
119 Self::Constant(val) => Ok(*val),
120 Self::ComplexConstant(val) => {
121 if val.im.abs() < 1e-12 {
122 Ok(val.re)
123 } else {
124 Err(crate::error::QuantRS2Error::InvalidInput(
125 "Cannot evaluate complex parameter to real number".to_string(),
126 ))
127 }
128 }
129 Self::Symbol(sym) => sym.value.map_or_else(
130 || {
131 variables.get(&sym.name).copied().ok_or_else(|| {
132 crate::error::QuantRS2Error::InvalidInput(format!(
133 "Variable '{}' not found",
134 sym.name
135 ))
136 })
137 },
138 Ok,
139 ),
140 Self::Symbolic(expr) => expr.evaluate(variables),
141 }
142 }
143
144 pub fn evaluate_complex(
146 &self,
147 variables: &HashMap<String, Complex64>,
148 ) -> QuantRS2Result<Complex64> {
149 match self {
150 Self::Constant(val) => Ok(Complex64::new(*val, 0.0)),
151 Self::ComplexConstant(val) => Ok(*val),
152 Self::Symbol(sym) => sym.value.map_or_else(
153 || {
154 variables.get(&sym.name).copied().ok_or_else(|| {
155 crate::error::QuantRS2Error::InvalidInput(format!(
156 "Variable '{}' not found",
157 sym.name
158 ))
159 })
160 },
161 |value| Ok(Complex64::new(value, 0.0)),
162 ),
163 Self::Symbolic(expr) => expr.evaluate_complex(variables),
164 }
165 }
166
167 pub fn variables(&self) -> Vec<String> {
169 match self {
170 Self::Constant(_) | Self::ComplexConstant(_) => Vec::new(),
171 Self::Symbol(sym) => {
172 if sym.value.is_some() {
173 Vec::new()
174 } else {
175 vec![sym.name.clone()]
176 }
177 }
178 Self::Symbolic(expr) => expr.variables(),
179 }
180 }
181
182 pub fn substitute(&self, substitutions: &HashMap<String, Self>) -> QuantRS2Result<Self> {
184 match self {
185 Self::Constant(_) | Self::ComplexConstant(_) => Ok(self.clone()),
186 Self::Symbol(sym) => Ok(substitutions
187 .get(&sym.name)
188 .map_or_else(|| self.clone(), |r| r.clone())),
189 Self::Symbolic(expr) => {
190 let symbolic_subs: HashMap<String, SymbolicExpression> = substitutions
192 .iter()
193 .map(|(k, v)| (k.clone(), v.to_symbolic_expression()))
194 .collect();
195
196 let new_expr = expr.substitute(&symbolic_subs)?;
197 Ok(Self::Symbolic(new_expr))
198 }
199 }
200 }
201
202 pub fn to_symbolic_expression(&self) -> SymbolicExpression {
204 match self {
205 Self::Constant(val) => SymbolicExpression::constant(*val),
206 Self::ComplexConstant(val) => SymbolicExpression::complex_constant(*val),
207 Self::Symbol(sym) => sym.value.map_or_else(
208 || SymbolicExpression::variable(&sym.name),
209 SymbolicExpression::constant,
210 ),
211 Self::Symbolic(expr) => expr.clone(),
212 }
213 }
214
215 #[cfg(feature = "symbolic")]
217 pub fn diff(&self, var: &str) -> QuantRS2Result<Self> {
218 use crate::symbolic::calculus;
219
220 let expr = self.to_symbolic_expression();
221 let diff_expr = calculus::diff(&expr, var)?;
222 Ok(Self::Symbolic(diff_expr))
223 }
224
225 #[cfg(feature = "symbolic")]
227 pub fn integrate(&self, var: &str) -> QuantRS2Result<Self> {
228 use crate::symbolic::calculus;
229
230 let expr = self.to_symbolic_expression();
231 let int_expr = calculus::integrate(&expr, var)?;
232 Ok(Self::Symbolic(int_expr))
233 }
234}
235
236impl From<f64> for Parameter {
237 fn from(value: f64) -> Self {
238 Self::Constant(value)
239 }
240}
241
242impl From<Complex64> for Parameter {
243 fn from(value: Complex64) -> Self {
244 if value.im == 0.0 {
245 Self::Constant(value.re)
246 } else {
247 Self::ComplexConstant(value)
248 }
249 }
250}
251
252impl From<SymbolicExpression> for Parameter {
253 fn from(expr: SymbolicExpression) -> Self {
254 Self::Symbolic(expr)
255 }
256}
257
258impl From<&str> for Parameter {
259 fn from(name: &str) -> Self {
260 Self::variable(name)
261 }
262}
263
264impl Add for Parameter {
265 type Output = Self;
266
267 fn add(self, rhs: Self) -> Self::Output {
268 match (self, rhs) {
269 (Self::Constant(a), Self::Constant(b)) => Self::Constant(a + b),
270 (Self::ComplexConstant(a), Self::ComplexConstant(b)) => Self::ComplexConstant(a + b),
271 (Self::Constant(a), Self::ComplexConstant(b)) => {
272 Self::ComplexConstant(Complex64::new(a, 0.0) + b)
273 }
274 (Self::ComplexConstant(a), Self::Constant(b)) => {
275 Self::ComplexConstant(a + Complex64::new(b, 0.0))
276 }
277 (a, b) => {
278 let a_expr = a.to_symbolic_expression();
280 let b_expr = b.to_symbolic_expression();
281 Self::Symbolic(a_expr + b_expr)
282 }
283 }
284 }
285}
286
287impl Sub for Parameter {
288 type Output = Self;
289
290 fn sub(self, rhs: Self) -> Self::Output {
291 match (self, rhs) {
292 (Self::Constant(a), Self::Constant(b)) => Self::Constant(a - b),
293 (Self::ComplexConstant(a), Self::ComplexConstant(b)) => Self::ComplexConstant(a - b),
294 (Self::Constant(a), Self::ComplexConstant(b)) => {
295 Self::ComplexConstant(Complex64::new(a, 0.0) - b)
296 }
297 (Self::ComplexConstant(a), Self::Constant(b)) => {
298 Self::ComplexConstant(a - Complex64::new(b, 0.0))
299 }
300 (a, b) => {
301 let a_expr = a.to_symbolic_expression();
303 let b_expr = b.to_symbolic_expression();
304 Self::Symbolic(a_expr - b_expr)
305 }
306 }
307 }
308}
309
310impl Mul for Parameter {
311 type Output = Self;
312
313 fn mul(self, rhs: Self) -> Self::Output {
314 match (self, rhs) {
315 (Self::Constant(a), Self::Constant(b)) => Self::Constant(a * b),
316 (Self::ComplexConstant(a), Self::ComplexConstant(b)) => Self::ComplexConstant(a * b),
317 (Self::Constant(a), Self::ComplexConstant(b)) => {
318 Self::ComplexConstant(Complex64::new(a, 0.0) * b)
319 }
320 (Self::ComplexConstant(a), Self::Constant(b)) => {
321 Self::ComplexConstant(a * Complex64::new(b, 0.0))
322 }
323 (a, b) => {
324 let a_expr = a.to_symbolic_expression();
326 let b_expr = b.to_symbolic_expression();
327 Self::Symbolic(a_expr * b_expr)
328 }
329 }
330 }
331}
332
333impl Div for Parameter {
334 type Output = Self;
335
336 fn div(self, rhs: Self) -> Self::Output {
337 match (self, rhs) {
338 (Self::Constant(a), Self::Constant(b)) => Self::Constant(a / b),
339 (Self::ComplexConstant(a), Self::ComplexConstant(b)) => Self::ComplexConstant(a / b),
340 (Self::Constant(a), Self::ComplexConstant(b)) => {
341 Self::ComplexConstant(Complex64::new(a, 0.0) / b)
342 }
343 (Self::ComplexConstant(a), Self::Constant(b)) => {
344 Self::ComplexConstant(a / Complex64::new(b, 0.0))
345 }
346 (a, b) => {
347 let a_expr = a.to_symbolic_expression();
349 let b_expr = b.to_symbolic_expression();
350 Self::Symbolic(a_expr / b_expr)
351 }
352 }
353 }
354}
355
356#[derive(Debug, Clone)]
358pub struct SymbolicParameter {
359 pub name: String,
361
362 pub value: Option<f64>,
364}
365
366impl SymbolicParameter {
367 pub fn new(name: &str) -> Self {
369 Self {
370 name: name.to_string(),
371 value: None,
372 }
373 }
374
375 pub fn with_value(name: &str, value: f64) -> Self {
377 Self {
378 name: name.to_string(),
379 value: Some(value),
380 }
381 }
382
383 pub const fn set_value(&mut self, value: f64) {
385 self.value = Some(value);
386 }
387
388 pub const fn clear_value(&mut self) {
390 self.value = None;
391 }
392}
393
394pub trait ParametricGate: GateOp {
399 fn parameters(&self) -> Vec<Parameter>;
401
402 fn parameter_names(&self) -> Vec<String>;
404
405 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>>;
407
408 fn with_parameter_at(
410 &self,
411 index: usize,
412 param: Parameter,
413 ) -> QuantRS2Result<Box<dyn ParametricGate>>;
414
415 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
417
418 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
420}
421
422#[derive(Debug, Clone)]
424pub struct ParametricRotationX {
425 pub target: QubitId,
427
428 pub theta: Parameter,
430}
431
432impl ParametricRotationX {
433 pub const fn new(target: QubitId, theta: f64) -> Self {
435 Self {
436 target,
437 theta: Parameter::constant(theta),
438 }
439 }
440
441 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
443 Self {
444 target,
445 theta: Parameter::symbol(name),
446 }
447 }
448}
449
450impl GateOp for ParametricRotationX {
451 fn name(&self) -> &'static str {
452 "RX"
453 }
454
455 fn qubits(&self) -> Vec<QubitId> {
456 vec![self.target]
457 }
458
459 fn is_parameterized(&self) -> bool {
460 true
461 }
462
463 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
464 self.theta.value().map_or_else(
465 || {
466 Err(crate::error::QuantRS2Error::UnsupportedOperation(
467 "Cannot generate matrix for RX gate with unbound symbolic parameter".into(),
468 ))
469 },
470 |theta| {
471 let cos = (theta / 2.0).cos();
472 let sin = (theta / 2.0).sin();
473 Ok(vec![
474 Complex64::new(cos, 0.0),
475 Complex64::new(0.0, -sin),
476 Complex64::new(0.0, -sin),
477 Complex64::new(cos, 0.0),
478 ])
479 },
480 )
481 }
482
483 fn as_any(&self) -> &dyn std::any::Any {
484 self
485 }
486
487 fn clone_gate(&self) -> Box<dyn GateOp> {
488 Box::new(self.clone())
489 }
490}
491
492impl ParametricGate for ParametricRotationX {
493 fn parameters(&self) -> Vec<Parameter> {
494 vec![self.theta.clone()]
495 }
496
497 fn parameter_names(&self) -> Vec<String> {
498 match self.theta {
499 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
500 _ => Vec::new(),
501 }
502 }
503
504 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
505 if params.len() != 1 {
506 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
507 "RotationX expects 1 parameter, got {}",
508 params.len()
509 )));
510 }
511 Ok(Box::new(Self {
512 target: self.target,
513 theta: params[0].clone(),
514 }))
515 }
516
517 fn with_parameter_at(
518 &self,
519 index: usize,
520 param: Parameter,
521 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
522 if index != 0 {
523 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
524 "RotationX has only 1 parameter, got index {index}"
525 )));
526 }
527 Ok(Box::new(Self {
528 target: self.target,
529 theta: param,
530 }))
531 }
532
533 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
534 match self.theta {
535 Parameter::Symbol(ref sym) => {
536 for (name, value) in values {
537 if sym.name == *name {
538 return Ok(Box::new(Self {
539 target: self.target,
540 theta: Parameter::Symbol(SymbolicParameter::with_value(
541 &sym.name, *value,
542 )),
543 }));
544 }
545 }
546 Ok(Box::new(self.clone()))
548 }
549 _ => Ok(Box::new(self.clone())), }
551 }
552
553 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
554 self.assign(values)
555 }
556}
557
558#[derive(Debug, Clone)]
560pub struct ParametricRotationY {
561 pub target: QubitId,
563
564 pub theta: Parameter,
566}
567
568impl ParametricRotationY {
569 pub const fn new(target: QubitId, theta: f64) -> Self {
571 Self {
572 target,
573 theta: Parameter::constant(theta),
574 }
575 }
576
577 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
579 Self {
580 target,
581 theta: Parameter::symbol(name),
582 }
583 }
584}
585
586impl GateOp for ParametricRotationY {
587 fn name(&self) -> &'static str {
588 "RY"
589 }
590
591 fn qubits(&self) -> Vec<QubitId> {
592 vec![self.target]
593 }
594
595 fn is_parameterized(&self) -> bool {
596 true
597 }
598
599 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
600 self.theta.value().map_or_else(
601 || {
602 Err(crate::error::QuantRS2Error::UnsupportedOperation(
603 "Cannot generate matrix for RY gate with unbound symbolic parameter".into(),
604 ))
605 },
606 |theta| {
607 let cos = (theta / 2.0).cos();
608 let sin = (theta / 2.0).sin();
609 Ok(vec![
610 Complex64::new(cos, 0.0),
611 Complex64::new(-sin, 0.0),
612 Complex64::new(sin, 0.0),
613 Complex64::new(cos, 0.0),
614 ])
615 },
616 )
617 }
618
619 fn as_any(&self) -> &dyn std::any::Any {
620 self
621 }
622
623 fn clone_gate(&self) -> Box<dyn GateOp> {
624 Box::new(self.clone())
625 }
626}
627
628impl ParametricGate for ParametricRotationY {
629 fn parameters(&self) -> Vec<Parameter> {
630 vec![self.theta.clone()]
631 }
632
633 fn parameter_names(&self) -> Vec<String> {
634 match self.theta {
635 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
636 _ => Vec::new(),
637 }
638 }
639
640 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
641 if params.len() != 1 {
642 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
643 "RotationY expects 1 parameter, got {}",
644 params.len()
645 )));
646 }
647 Ok(Box::new(Self {
648 target: self.target,
649 theta: params[0].clone(),
650 }))
651 }
652
653 fn with_parameter_at(
654 &self,
655 index: usize,
656 param: Parameter,
657 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
658 if index != 0 {
659 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
660 "RotationY has only 1 parameter, got index {index}"
661 )));
662 }
663 Ok(Box::new(Self {
664 target: self.target,
665 theta: param,
666 }))
667 }
668
669 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
670 match self.theta {
671 Parameter::Symbol(ref sym) => {
672 for (name, value) in values {
673 if sym.name == *name {
674 return Ok(Box::new(Self {
675 target: self.target,
676 theta: Parameter::Symbol(SymbolicParameter::with_value(
677 &sym.name, *value,
678 )),
679 }));
680 }
681 }
682 Ok(Box::new(self.clone()))
684 }
685 _ => Ok(Box::new(self.clone())), }
687 }
688
689 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
690 self.assign(values)
691 }
692}
693
694#[derive(Debug, Clone)]
696pub struct ParametricRotationZ {
697 pub target: QubitId,
699
700 pub theta: Parameter,
702}
703
704impl ParametricRotationZ {
705 pub const fn new(target: QubitId, theta: f64) -> Self {
707 Self {
708 target,
709 theta: Parameter::constant(theta),
710 }
711 }
712
713 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
715 Self {
716 target,
717 theta: Parameter::symbol(name),
718 }
719 }
720}
721
722impl GateOp for ParametricRotationZ {
723 fn name(&self) -> &'static str {
724 "RZ"
725 }
726
727 fn qubits(&self) -> Vec<QubitId> {
728 vec![self.target]
729 }
730
731 fn is_parameterized(&self) -> bool {
732 true
733 }
734
735 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
736 self.theta.value().map_or_else(
737 || {
738 Err(crate::error::QuantRS2Error::UnsupportedOperation(
739 "Cannot generate matrix for RZ gate with unbound symbolic parameter".into(),
740 ))
741 },
742 |theta| {
743 let phase = Complex64::new(0.0, -theta / 2.0).exp();
744 let phase_conj = Complex64::new(0.0, theta / 2.0).exp();
745 Ok(vec![
746 phase_conj,
747 Complex64::new(0.0, 0.0),
748 Complex64::new(0.0, 0.0),
749 phase,
750 ])
751 },
752 )
753 }
754
755 fn as_any(&self) -> &dyn std::any::Any {
756 self
757 }
758
759 fn clone_gate(&self) -> Box<dyn GateOp> {
760 Box::new(self.clone())
761 }
762}
763
764impl ParametricGate for ParametricRotationZ {
765 fn parameters(&self) -> Vec<Parameter> {
766 vec![self.theta.clone()]
767 }
768
769 fn parameter_names(&self) -> Vec<String> {
770 match self.theta {
771 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
772 _ => Vec::new(),
773 }
774 }
775
776 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
777 if params.len() != 1 {
778 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
779 "RotationZ expects 1 parameter, got {}",
780 params.len()
781 )));
782 }
783 Ok(Box::new(Self {
784 target: self.target,
785 theta: params[0].clone(),
786 }))
787 }
788
789 fn with_parameter_at(
790 &self,
791 index: usize,
792 param: Parameter,
793 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
794 if index != 0 {
795 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
796 "RotationZ has only 1 parameter, got index {index}"
797 )));
798 }
799 Ok(Box::new(Self {
800 target: self.target,
801 theta: param,
802 }))
803 }
804
805 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
806 match self.theta {
807 Parameter::Symbol(ref sym) => {
808 for (name, value) in values {
809 if sym.name == *name {
810 return Ok(Box::new(Self {
811 target: self.target,
812 theta: Parameter::Symbol(SymbolicParameter::with_value(
813 &sym.name, *value,
814 )),
815 }));
816 }
817 }
818 Ok(Box::new(self.clone()))
820 }
821 _ => Ok(Box::new(self.clone())), }
823 }
824
825 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
826 self.assign(values)
827 }
828}
829
830#[derive(Debug, Clone)]
832pub struct ParametricU {
833 pub target: QubitId,
835
836 pub theta: Parameter,
838
839 pub phi: Parameter,
841
842 pub lambda: Parameter,
844}
845
846impl ParametricU {
847 pub const fn new(target: QubitId, theta: f64, phi: f64, lambda: f64) -> Self {
849 Self {
850 target,
851 theta: Parameter::constant(theta),
852 phi: Parameter::constant(phi),
853 lambda: Parameter::constant(lambda),
854 }
855 }
856
857 pub fn new_symbolic(
859 target: QubitId,
860 theta_name: &str,
861 phi_name: &str,
862 lambda_name: &str,
863 ) -> Self {
864 Self {
865 target,
866 theta: Parameter::symbol(theta_name),
867 phi: Parameter::symbol(phi_name),
868 lambda: Parameter::symbol(lambda_name),
869 }
870 }
871}
872
873impl GateOp for ParametricU {
874 fn name(&self) -> &'static str {
875 "U"
876 }
877
878 fn qubits(&self) -> Vec<QubitId> {
879 vec![self.target]
880 }
881
882 fn is_parameterized(&self) -> bool {
883 true
884 }
885
886 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
887 if let (Some(theta), Some(phi), Some(lambda)) =
888 (self.theta.value(), self.phi.value(), self.lambda.value())
889 {
890 let cos = (theta / 2.0).cos();
891 let sin = (theta / 2.0).sin();
892
893 let e_phi = Complex64::new(0.0, phi).exp();
894 let e_lambda = Complex64::new(0.0, lambda).exp();
895
896 Ok(vec![
897 Complex64::new(cos, 0.0),
898 -sin * e_lambda,
899 sin * e_phi,
900 cos * e_phi * e_lambda,
901 ])
902 } else {
903 Err(crate::error::QuantRS2Error::UnsupportedOperation(
904 "Cannot generate matrix for U gate with unbound symbolic parameters".into(),
905 ))
906 }
907 }
908
909 fn as_any(&self) -> &dyn std::any::Any {
910 self
911 }
912
913 fn clone_gate(&self) -> Box<dyn GateOp> {
914 Box::new(self.clone())
915 }
916}
917
918impl ParametricGate for ParametricU {
919 fn parameters(&self) -> Vec<Parameter> {
920 vec![self.theta.clone(), self.phi.clone(), self.lambda.clone()]
921 }
922
923 fn parameter_names(&self) -> Vec<String> {
924 let mut names = Vec::new();
925
926 if let Parameter::Symbol(ref sym) = self.theta {
927 names.push(sym.name.clone());
928 }
929
930 if let Parameter::Symbol(ref sym) = self.phi {
931 names.push(sym.name.clone());
932 }
933
934 if let Parameter::Symbol(ref sym) = self.lambda {
935 names.push(sym.name.clone());
936 }
937
938 names
939 }
940
941 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
942 if params.len() != 3 {
943 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
944 "U gate expects 3 parameters, got {}",
945 params.len()
946 )));
947 }
948 Ok(Box::new(Self {
949 target: self.target,
950 theta: params[0].clone(),
951 phi: params[1].clone(),
952 lambda: params[2].clone(),
953 }))
954 }
955
956 fn with_parameter_at(
957 &self,
958 index: usize,
959 param: Parameter,
960 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
961 match index {
962 0 => Ok(Box::new(Self {
963 target: self.target,
964 theta: param,
965 phi: self.phi.clone(),
966 lambda: self.lambda.clone(),
967 })),
968 1 => Ok(Box::new(Self {
969 target: self.target,
970 theta: self.theta.clone(),
971 phi: param,
972 lambda: self.lambda.clone(),
973 })),
974 2 => Ok(Box::new(Self {
975 target: self.target,
976 theta: self.theta.clone(),
977 phi: self.phi.clone(),
978 lambda: param,
979 })),
980 _ => Err(crate::error::QuantRS2Error::InvalidInput(format!(
981 "U gate has only 3 parameters, got index {index}"
982 ))),
983 }
984 }
985
986 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
987 let mut result = self.clone();
988
989 if let Parameter::Symbol(ref sym) = self.theta {
991 for (name, value) in values {
992 if sym.name == *name {
993 result.theta =
994 Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
995 break;
996 }
997 }
998 }
999
1000 if let Parameter::Symbol(ref sym) = self.phi {
1002 for (name, value) in values {
1003 if sym.name == *name {
1004 result.phi =
1005 Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
1006 break;
1007 }
1008 }
1009 }
1010
1011 if let Parameter::Symbol(ref sym) = self.lambda {
1013 for (name, value) in values {
1014 if sym.name == *name {
1015 result.lambda =
1016 Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
1017 break;
1018 }
1019 }
1020 }
1021
1022 Ok(Box::new(result))
1023 }
1024
1025 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1026 self.assign(values)
1027 }
1028}
1029
1030#[derive(Debug, Clone)]
1032pub struct ParametricCRX {
1033 pub control: QubitId,
1035
1036 pub target: QubitId,
1038
1039 pub theta: Parameter,
1041}
1042
1043impl ParametricCRX {
1044 pub const fn new(control: QubitId, target: QubitId, theta: f64) -> Self {
1046 Self {
1047 control,
1048 target,
1049 theta: Parameter::constant(theta),
1050 }
1051 }
1052
1053 pub fn new_symbolic(control: QubitId, target: QubitId, name: &str) -> Self {
1055 Self {
1056 control,
1057 target,
1058 theta: Parameter::symbol(name),
1059 }
1060 }
1061}
1062
1063impl GateOp for ParametricCRX {
1064 fn name(&self) -> &'static str {
1065 "CRX"
1066 }
1067
1068 fn qubits(&self) -> Vec<QubitId> {
1069 vec![self.control, self.target]
1070 }
1071
1072 fn is_parameterized(&self) -> bool {
1073 true
1074 }
1075
1076 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
1077 self.theta.value().map_or_else(
1078 || {
1079 Err(crate::error::QuantRS2Error::UnsupportedOperation(
1080 "Cannot generate matrix for CRX gate with unbound symbolic parameter".into(),
1081 ))
1082 },
1083 |theta| {
1084 let cos = (theta / 2.0).cos();
1085 let sin = (theta / 2.0).sin();
1086
1087 Ok(vec![
1088 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), ])
1105 },
1106 )
1107 }
1108
1109 fn as_any(&self) -> &dyn std::any::Any {
1110 self
1111 }
1112
1113 fn clone_gate(&self) -> Box<dyn GateOp> {
1114 Box::new(self.clone())
1115 }
1116}
1117
1118impl ParametricGate for ParametricCRX {
1119 fn parameters(&self) -> Vec<Parameter> {
1120 vec![self.theta.clone()]
1121 }
1122
1123 fn parameter_names(&self) -> Vec<String> {
1124 match self.theta {
1125 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
1126 _ => Vec::new(),
1127 }
1128 }
1129
1130 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1131 if params.len() != 1 {
1132 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
1133 "CRX expects 1 parameter, got {}",
1134 params.len()
1135 )));
1136 }
1137 Ok(Box::new(Self {
1138 control: self.control,
1139 target: self.target,
1140 theta: params[0].clone(),
1141 }))
1142 }
1143
1144 fn with_parameter_at(
1145 &self,
1146 index: usize,
1147 param: Parameter,
1148 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
1149 if index != 0 {
1150 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
1151 "CRX has only 1 parameter, got index {index}"
1152 )));
1153 }
1154 Ok(Box::new(Self {
1155 control: self.control,
1156 target: self.target,
1157 theta: param,
1158 }))
1159 }
1160
1161 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1162 match self.theta {
1163 Parameter::Symbol(ref sym) => {
1164 for (name, value) in values {
1165 if sym.name == *name {
1166 return Ok(Box::new(Self {
1167 control: self.control,
1168 target: self.target,
1169 theta: Parameter::Symbol(SymbolicParameter::with_value(
1170 &sym.name, *value,
1171 )),
1172 }));
1173 }
1174 }
1175 Ok(Box::new(self.clone()))
1177 }
1178 _ => Ok(Box::new(self.clone())), }
1180 }
1181
1182 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1183 self.assign(values)
1184 }
1185}
1186
1187#[derive(Debug, Clone)]
1189pub struct ParametricPhaseShift {
1190 pub target: QubitId,
1192
1193 pub phi: Parameter,
1195}
1196
1197impl ParametricPhaseShift {
1198 pub const fn new(target: QubitId, phi: f64) -> Self {
1200 Self {
1201 target,
1202 phi: Parameter::constant(phi),
1203 }
1204 }
1205
1206 pub fn new_symbolic(target: QubitId, name: &str) -> Self {
1208 Self {
1209 target,
1210 phi: Parameter::symbol(name),
1211 }
1212 }
1213}
1214
1215impl GateOp for ParametricPhaseShift {
1216 fn name(&self) -> &'static str {
1217 "P"
1218 }
1219
1220 fn qubits(&self) -> Vec<QubitId> {
1221 vec![self.target]
1222 }
1223
1224 fn is_parameterized(&self) -> bool {
1225 true
1226 }
1227
1228 fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
1229 self.phi.value().map_or_else(
1230 || {
1231 Err(crate::error::QuantRS2Error::UnsupportedOperation(
1232 "Cannot generate matrix for phase shift gate with unbound symbolic parameter"
1233 .into(),
1234 ))
1235 },
1236 |phi| {
1237 let phase = Complex64::new(phi.cos(), phi.sin());
1238 Ok(vec![
1239 Complex64::new(1.0, 0.0),
1240 Complex64::new(0.0, 0.0),
1241 Complex64::new(0.0, 0.0),
1242 phase,
1243 ])
1244 },
1245 )
1246 }
1247
1248 fn as_any(&self) -> &dyn std::any::Any {
1249 self
1250 }
1251
1252 fn clone_gate(&self) -> Box<dyn GateOp> {
1253 Box::new(self.clone())
1254 }
1255}
1256
1257impl ParametricGate for ParametricPhaseShift {
1258 fn parameters(&self) -> Vec<Parameter> {
1259 vec![self.phi.clone()]
1260 }
1261
1262 fn parameter_names(&self) -> Vec<String> {
1263 match self.phi {
1264 Parameter::Symbol(ref sym) => vec![sym.name.clone()],
1265 _ => Vec::new(),
1266 }
1267 }
1268
1269 fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1270 if params.len() != 1 {
1271 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
1272 "Phase shift gate expects 1 parameter, got {}",
1273 params.len()
1274 )));
1275 }
1276 Ok(Box::new(Self {
1277 target: self.target,
1278 phi: params[0].clone(),
1279 }))
1280 }
1281
1282 fn with_parameter_at(
1283 &self,
1284 index: usize,
1285 param: Parameter,
1286 ) -> QuantRS2Result<Box<dyn ParametricGate>> {
1287 if index != 0 {
1288 return Err(crate::error::QuantRS2Error::InvalidInput(format!(
1289 "Phase shift gate has only 1 parameter, got index {index}"
1290 )));
1291 }
1292 Ok(Box::new(Self {
1293 target: self.target,
1294 phi: param,
1295 }))
1296 }
1297
1298 fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1299 match self.phi {
1300 Parameter::Symbol(ref sym) => {
1301 for (name, value) in values {
1302 if sym.name == *name {
1303 return Ok(Box::new(Self {
1304 target: self.target,
1305 phi: Parameter::Symbol(SymbolicParameter::with_value(
1306 &sym.name, *value,
1307 )),
1308 }));
1309 }
1310 }
1311 Ok(Box::new(self.clone()))
1313 }
1314 _ => Ok(Box::new(self.clone())), }
1316 }
1317
1318 fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1319 self.assign(values)
1320 }
1321}
1322
1323pub mod utils {
1325 use super::*;
1326 use crate::gate::{multi, single};
1327
1328 pub fn parametrize_rotation_gate(gate: &dyn GateOp) -> Option<Box<dyn ParametricGate>> {
1330 if !gate.is_parameterized() {
1331 return None;
1332 }
1333
1334 if let Some(rx) = gate.as_any().downcast_ref::<single::RotationX>() {
1335 Some(Box::new(ParametricRotationX::new(rx.target, rx.theta)))
1336 } else if let Some(ry) = gate.as_any().downcast_ref::<single::RotationY>() {
1337 Some(Box::new(ParametricRotationY::new(ry.target, ry.theta)))
1338 } else if let Some(rz) = gate.as_any().downcast_ref::<single::RotationZ>() {
1339 Some(Box::new(ParametricRotationZ::new(rz.target, rz.theta)))
1340 } else if let Some(crx) = gate.as_any().downcast_ref::<multi::CRX>() {
1341 Some(Box::new(ParametricCRX::new(
1342 crx.control,
1343 crx.target,
1344 crx.theta,
1345 )))
1346 } else {
1347 None
1348 }
1349 }
1350
1351 pub fn symbolize_parameter(param: f64, name: &str) -> Parameter {
1353 Parameter::symbol_with_value(name, param)
1354 }
1355
1356 pub fn parameters_approx_eq(p1: &Parameter, p2: &Parameter, epsilon: f64) -> bool {
1358 match (p1.value(), p2.value()) {
1359 (Some(v1), Some(v2)) => (v1 - v2).abs() < epsilon,
1360 _ => false,
1361 }
1362 }
1363}