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