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