quantrs2_core/
parametric.rs

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/// Parameter value representation for parametric gates
11#[derive(Debug, Clone)]
12pub enum Parameter {
13    /// Constant floating-point value
14    Constant(f64),
15
16    /// Complex constant value
17    ComplexConstant(Complex64),
18
19    /// Symbolic parameter with a name and optional value (legacy)
20    Symbol(SymbolicParameter),
21
22    /// Full symbolic expression using SymEngine
23    Symbolic(SymbolicExpression),
24}
25
26impl Parameter {
27    /// Create a new constant parameter
28    pub fn constant(value: f64) -> Self {
29        Parameter::Constant(value)
30    }
31
32    /// Create a new complex constant parameter
33    pub fn complex_constant(value: Complex64) -> Self {
34        Parameter::ComplexConstant(value)
35    }
36
37    /// Create a new symbolic parameter (legacy)
38    pub fn symbol(name: &str) -> Self {
39        Parameter::Symbol(SymbolicParameter::new(name))
40    }
41
42    /// Create a new symbolic parameter with a value (legacy)
43    pub fn symbol_with_value(name: &str, value: f64) -> Self {
44        Parameter::Symbol(SymbolicParameter::with_value(name, value))
45    }
46
47    /// Create a symbolic expression parameter
48    pub fn symbolic(expr: SymbolicExpression) -> Self {
49        Parameter::Symbolic(expr)
50    }
51
52    /// Create a symbolic variable
53    pub fn variable(name: &str) -> Self {
54        Parameter::Symbolic(SymbolicExpression::variable(name))
55    }
56
57    /// Parse a parameter from a string expression
58    pub fn parse(expr: &str) -> QuantRS2Result<Self> {
59        // Try to parse as a number first
60        if let Ok(value) = expr.parse::<f64>() {
61            return Ok(Parameter::Constant(value));
62        }
63
64        // Otherwise parse as symbolic expression
65        let symbolic_expr = SymbolicExpression::parse(expr)?;
66        Ok(Parameter::Symbolic(symbolic_expr))
67    }
68
69    /// Get the value of the parameter, if available
70    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 // Cannot convert complex to real
78                }
79            }
80            Parameter::Symbol(sym) => sym.value,
81            Parameter::Symbolic(expr) => {
82                // Try to evaluate with empty variables
83                expr.evaluate(&HashMap::new()).ok()
84            }
85        }
86    }
87
88    /// Get the complex value of the parameter, if available
89    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                // Try to evaluate with empty variables
96                expr.evaluate_complex(&HashMap::new()).ok()
97            }
98        }
99    }
100
101    /// Check if the parameter has a concrete value
102    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    /// Evaluate the parameter with given variable values
111    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    /// Evaluate the parameter with given variable values (complex)
140    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    /// Get all variable names in the parameter
164    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    /// Substitute variables with expressions
179    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                // Convert Parameter substitutions to SymbolicExpression substitutions
191                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    /// Convert parameter to SymbolicExpression
203    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    /// Differentiate the parameter with respect to a variable
219    #[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    /// Integrate the parameter with respect to a variable
229    #[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                // Convert to symbolic expressions and add
284                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                // Convert to symbolic expressions and subtract
309                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                // Convert to symbolic expressions and multiply
334                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                // Convert to symbolic expressions and divide
359                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/// Symbolic parameter that can be used in parametric gates
368#[derive(Debug, Clone)]
369pub struct SymbolicParameter {
370    /// Name of the parameter
371    pub name: String,
372
373    /// Optional value of the parameter
374    pub value: Option<f64>,
375}
376
377impl SymbolicParameter {
378    /// Create a new symbolic parameter without a value
379    pub fn new(name: &str) -> Self {
380        SymbolicParameter {
381            name: name.to_string(),
382            value: None,
383        }
384    }
385
386    /// Create a new symbolic parameter with a value
387    pub fn with_value(name: &str, value: f64) -> Self {
388        SymbolicParameter {
389            name: name.to_string(),
390            value: Some(value),
391        }
392    }
393
394    /// Set the value of the parameter
395    pub fn set_value(&mut self, value: f64) {
396        self.value = Some(value);
397    }
398
399    /// Clear the value of the parameter
400    pub fn clear_value(&mut self) {
401        self.value = None;
402    }
403}
404
405// Note: Cannot implement Copy because String doesn't implement Copy
406// Use Clone instead (already implemented above)
407
408/// Trait for parametric gates that extends GateOp with parameter-related functionality
409pub trait ParametricGate: GateOp {
410    /// Returns the parameters of the gate
411    fn parameters(&self) -> Vec<Parameter>;
412
413    /// Returns the names of the parameters
414    fn parameter_names(&self) -> Vec<String>;
415
416    /// Returns a new gate with updated parameters
417    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>>;
418
419    /// Returns a new gate with updated parameter at the specified index
420    fn with_parameter_at(
421        &self,
422        index: usize,
423        param: Parameter,
424    ) -> QuantRS2Result<Box<dyn ParametricGate>>;
425
426    /// Assigns values to symbolic parameters
427    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
428
429    /// Returns the gate with all parameters set to the specified values
430    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
431}
432
433/// Specialized implementation of rotation gates around the X-axis
434#[derive(Debug, Clone)]
435pub struct ParametricRotationX {
436    /// Target qubit
437    pub target: QubitId,
438
439    /// Rotation angle parameter
440    pub theta: Parameter,
441}
442
443impl ParametricRotationX {
444    /// Create a new X-rotation gate with a constant angle
445    pub fn new(target: QubitId, theta: f64) -> Self {
446        ParametricRotationX {
447            target,
448            theta: Parameter::constant(theta),
449        }
450    }
451
452    /// Create a new X-rotation gate with a symbolic angle
453    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                // Parameter not found in values, return a clone of the original gate
556                Ok(Box::new(self.clone()))
557            }
558            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
559        }
560    }
561
562    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
563        self.assign(values)
564    }
565}
566
567/// Specialized implementation of rotation gates around the Y-axis
568#[derive(Debug, Clone)]
569pub struct ParametricRotationY {
570    /// Target qubit
571    pub target: QubitId,
572
573    /// Rotation angle parameter
574    pub theta: Parameter,
575}
576
577impl ParametricRotationY {
578    /// Create a new Y-rotation gate with a constant angle
579    pub fn new(target: QubitId, theta: f64) -> Self {
580        ParametricRotationY {
581            target,
582            theta: Parameter::constant(theta),
583        }
584    }
585
586    /// Create a new Y-rotation gate with a symbolic angle
587    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                // Parameter not found in values, return a clone of the original gate
690                Ok(Box::new(self.clone()))
691            }
692            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
693        }
694    }
695
696    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
697        self.assign(values)
698    }
699}
700
701/// Specialized implementation of rotation gates around the Z-axis
702#[derive(Debug, Clone)]
703pub struct ParametricRotationZ {
704    /// Target qubit
705    pub target: QubitId,
706
707    /// Rotation angle parameter
708    pub theta: Parameter,
709}
710
711impl ParametricRotationZ {
712    /// Create a new Z-rotation gate with a constant angle
713    pub fn new(target: QubitId, theta: f64) -> Self {
714        ParametricRotationZ {
715            target,
716            theta: Parameter::constant(theta),
717        }
718    }
719
720    /// Create a new Z-rotation gate with a symbolic angle
721    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                // Parameter not found in values, return a clone of the original gate
824                Ok(Box::new(self.clone()))
825            }
826            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
827        }
828    }
829
830    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
831        self.assign(values)
832    }
833}
834
835/// Specialized implementation of a general U-gate (parameterized single-qubit gate)
836#[derive(Debug, Clone)]
837pub struct ParametricU {
838    /// Target qubit
839    pub target: QubitId,
840
841    /// Theta parameter (rotation angle)
842    pub theta: Parameter,
843
844    /// Phi parameter (phase angle)
845    pub phi: Parameter,
846
847    /// Lambda parameter (phase angle)
848    pub lambda: Parameter,
849}
850
851impl ParametricU {
852    /// Create a new U-gate with constant angles
853    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    /// Create a new U-gate with symbolic angles
863    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        // Update theta if it's a symbolic parameter
996        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        // Update phi if it's a symbolic parameter
1007        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        // Update lambda if it's a symbolic parameter
1018        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/// Specialized implementation of controlled parametric rotation around X-axis
1037#[derive(Debug, Clone)]
1038pub struct ParametricCRX {
1039    /// Control qubit
1040    pub control: QubitId,
1041
1042    /// Target qubit
1043    pub target: QubitId,
1044
1045    /// Rotation angle parameter
1046    pub theta: Parameter,
1047}
1048
1049impl ParametricCRX {
1050    /// Create a new CRX gate with a constant angle
1051    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    /// Create a new CRX gate with a symbolic angle
1060    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),  // (0,0)
1089                Complex64::new(0.0, 0.0),  // (0,1)
1090                Complex64::new(0.0, 0.0),  // (0,2)
1091                Complex64::new(0.0, 0.0),  // (0,3)
1092                Complex64::new(0.0, 0.0),  // (1,0)
1093                Complex64::new(1.0, 0.0),  // (1,1)
1094                Complex64::new(0.0, 0.0),  // (1,2)
1095                Complex64::new(0.0, 0.0),  // (1,3)
1096                Complex64::new(0.0, 0.0),  // (2,0)
1097                Complex64::new(0.0, 0.0),  // (2,1)
1098                Complex64::new(cos, 0.0),  // (2,2)
1099                Complex64::new(0.0, -sin), // (2,3)
1100                Complex64::new(0.0, 0.0),  // (3,0)
1101                Complex64::new(0.0, 0.0),  // (3,1)
1102                Complex64::new(0.0, -sin), // (3,2)
1103                Complex64::new(cos, 0.0),  // (3,3)
1104            ])
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                // Parameter not found in values, return a clone of the original gate
1180                Ok(Box::new(self.clone()))
1181            }
1182            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
1183        }
1184    }
1185
1186    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1187        self.assign(values)
1188    }
1189}
1190
1191/// Phase shift gate with a parameterized phase
1192#[derive(Debug, Clone)]
1193pub struct ParametricPhaseShift {
1194    /// Target qubit
1195    pub target: QubitId,
1196
1197    /// Phase parameter
1198    pub phi: Parameter,
1199}
1200
1201impl ParametricPhaseShift {
1202    /// Create a new phase shift gate with a constant phase
1203    pub fn new(target: QubitId, phi: f64) -> Self {
1204        ParametricPhaseShift {
1205            target,
1206            phi: Parameter::constant(phi),
1207        }
1208    }
1209
1210    /// Create a new phase shift gate with a symbolic phase
1211    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                // Parameter not found in values, return a clone of the original gate
1314                Ok(Box::new(self.clone()))
1315            }
1316            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
1317        }
1318    }
1319
1320    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1321        self.assign(values)
1322    }
1323}
1324
1325/// Module for utilities related to parametric gates
1326pub mod utils {
1327    use super::*;
1328    use crate::gate::{multi, single};
1329
1330    /// Helper function to create a parametric gate from a standard gate
1331    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    /// Convert a parameter value to a symbolic parameter
1354    pub fn symbolize_parameter(param: f64, name: &str) -> Parameter {
1355        Parameter::symbol_with_value(name, param)
1356    }
1357
1358    /// Check if two parameters are approximately equal
1359    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}