quantrs2_core/
parametric.rs

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/// 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 const fn constant(value: f64) -> Self {
29        Self::Constant(value)
30    }
31
32    /// Create a new complex constant parameter
33    pub const fn complex_constant(value: Complex64) -> Self {
34        Self::ComplexConstant(value)
35    }
36
37    /// Create a new symbolic parameter (legacy)
38    pub fn symbol(name: &str) -> Self {
39        Self::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        Self::Symbol(SymbolicParameter::with_value(name, value))
45    }
46
47    /// Create a symbolic expression parameter
48    pub const fn symbolic(expr: SymbolicExpression) -> Self {
49        Self::Symbolic(expr)
50    }
51
52    /// Create a symbolic variable
53    pub fn variable(name: &str) -> Self {
54        Self::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(Self::Constant(value));
62        }
63
64        // Otherwise parse as symbolic expression
65        let symbolic_expr = SymbolicExpression::parse(expr)?;
66        Ok(Self::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            Self::Constant(val) => Some(*val),
73            Self::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            Self::Symbol(sym) => sym.value,
81            Self::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            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                // 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 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    /// Evaluate the parameter with given variable values
111    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    /// Evaluate the parameter with given variable values (complex)
139    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    /// Get all variable names in the parameter
162    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    /// Substitute variables with expressions
177    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                // Convert Parameter substitutions to SymbolicExpression substitutions
185                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    /// Convert parameter to SymbolicExpression
197    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    /// Differentiate the parameter with respect to a variable
210    #[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    /// Integrate the parameter with respect to a variable
220    #[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                // Convert to symbolic expressions and add
273                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                // Convert to symbolic expressions and subtract
296                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                // Convert to symbolic expressions and multiply
319                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                // Convert to symbolic expressions and divide
342                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/// Symbolic parameter that can be used in parametric gates
351#[derive(Debug, Clone)]
352pub struct SymbolicParameter {
353    /// Name of the parameter
354    pub name: String,
355
356    /// Optional value of the parameter
357    pub value: Option<f64>,
358}
359
360impl SymbolicParameter {
361    /// Create a new symbolic parameter without a value
362    pub fn new(name: &str) -> Self {
363        Self {
364            name: name.to_string(),
365            value: None,
366        }
367    }
368
369    /// Create a new symbolic parameter with a value
370    pub fn with_value(name: &str, value: f64) -> Self {
371        Self {
372            name: name.to_string(),
373            value: Some(value),
374        }
375    }
376
377    /// Set the value of the parameter
378    pub const fn set_value(&mut self, value: f64) {
379        self.value = Some(value);
380    }
381
382    /// Clear the value of the parameter
383    pub const fn clear_value(&mut self) {
384        self.value = None;
385    }
386}
387
388// Note: Cannot implement Copy because String doesn't implement Copy
389// Use Clone instead (already implemented above)
390
391/// Trait for parametric gates that extends GateOp with parameter-related functionality
392pub trait ParametricGate: GateOp {
393    /// Returns the parameters of the gate
394    fn parameters(&self) -> Vec<Parameter>;
395
396    /// Returns the names of the parameters
397    fn parameter_names(&self) -> Vec<String>;
398
399    /// Returns a new gate with updated parameters
400    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>>;
401
402    /// Returns a new gate with updated parameter at the specified index
403    fn with_parameter_at(
404        &self,
405        index: usize,
406        param: Parameter,
407    ) -> QuantRS2Result<Box<dyn ParametricGate>>;
408
409    /// Assigns values to symbolic parameters
410    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
411
412    /// Returns the gate with all parameters set to the specified values
413    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
414}
415
416/// Specialized implementation of rotation gates around the X-axis
417#[derive(Debug, Clone)]
418pub struct ParametricRotationX {
419    /// Target qubit
420    pub target: QubitId,
421
422    /// Rotation angle parameter
423    pub theta: Parameter,
424}
425
426impl ParametricRotationX {
427    /// Create a new X-rotation gate with a constant angle
428    pub const fn new(target: QubitId, theta: f64) -> Self {
429        Self {
430            target,
431            theta: Parameter::constant(theta),
432        }
433    }
434
435    /// Create a new X-rotation gate with a symbolic angle
436    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                // Parameter not found in values, return a clone of the original gate
541                Ok(Box::new(self.clone()))
542            }
543            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
544        }
545    }
546
547    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
548        self.assign(values)
549    }
550}
551
552/// Specialized implementation of rotation gates around the Y-axis
553#[derive(Debug, Clone)]
554pub struct ParametricRotationY {
555    /// Target qubit
556    pub target: QubitId,
557
558    /// Rotation angle parameter
559    pub theta: Parameter,
560}
561
562impl ParametricRotationY {
563    /// Create a new Y-rotation gate with a constant angle
564    pub const fn new(target: QubitId, theta: f64) -> Self {
565        Self {
566            target,
567            theta: Parameter::constant(theta),
568        }
569    }
570
571    /// Create a new Y-rotation gate with a symbolic angle
572    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                // Parameter not found in values, return a clone of the original gate
677                Ok(Box::new(self.clone()))
678            }
679            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
680        }
681    }
682
683    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
684        self.assign(values)
685    }
686}
687
688/// Specialized implementation of rotation gates around the Z-axis
689#[derive(Debug, Clone)]
690pub struct ParametricRotationZ {
691    /// Target qubit
692    pub target: QubitId,
693
694    /// Rotation angle parameter
695    pub theta: Parameter,
696}
697
698impl ParametricRotationZ {
699    /// Create a new Z-rotation gate with a constant angle
700    pub const fn new(target: QubitId, theta: f64) -> Self {
701        Self {
702            target,
703            theta: Parameter::constant(theta),
704        }
705    }
706
707    /// Create a new Z-rotation gate with a symbolic angle
708    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                // Parameter not found in values, return a clone of the original gate
813                Ok(Box::new(self.clone()))
814            }
815            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
816        }
817    }
818
819    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
820        self.assign(values)
821    }
822}
823
824/// Specialized implementation of a general U-gate (parameterized single-qubit gate)
825#[derive(Debug, Clone)]
826pub struct ParametricU {
827    /// Target qubit
828    pub target: QubitId,
829
830    /// Theta parameter (rotation angle)
831    pub theta: Parameter,
832
833    /// Phi parameter (phase angle)
834    pub phi: Parameter,
835
836    /// Lambda parameter (phase angle)
837    pub lambda: Parameter,
838}
839
840impl ParametricU {
841    /// Create a new U-gate with constant angles
842    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    /// Create a new U-gate with symbolic angles
852    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        // Update theta if it's a symbolic parameter
984        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        // Update phi if it's a symbolic parameter
995        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        // Update lambda if it's a symbolic parameter
1006        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/// Specialized implementation of controlled parametric rotation around X-axis
1025#[derive(Debug, Clone)]
1026pub struct ParametricCRX {
1027    /// Control qubit
1028    pub control: QubitId,
1029
1030    /// Target qubit
1031    pub target: QubitId,
1032
1033    /// Rotation angle parameter
1034    pub theta: Parameter,
1035}
1036
1037impl ParametricCRX {
1038    /// Create a new CRX gate with a constant angle
1039    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    /// Create a new CRX gate with a symbolic angle
1048    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),  // (0,0)
1083                    Complex64::new(0.0, 0.0),  // (0,1)
1084                    Complex64::new(0.0, 0.0),  // (0,2)
1085                    Complex64::new(0.0, 0.0),  // (0,3)
1086                    Complex64::new(0.0, 0.0),  // (1,0)
1087                    Complex64::new(1.0, 0.0),  // (1,1)
1088                    Complex64::new(0.0, 0.0),  // (1,2)
1089                    Complex64::new(0.0, 0.0),  // (1,3)
1090                    Complex64::new(0.0, 0.0),  // (2,0)
1091                    Complex64::new(0.0, 0.0),  // (2,1)
1092                    Complex64::new(cos, 0.0),  // (2,2)
1093                    Complex64::new(0.0, -sin), // (2,3)
1094                    Complex64::new(0.0, 0.0),  // (3,0)
1095                    Complex64::new(0.0, 0.0),  // (3,1)
1096                    Complex64::new(0.0, -sin), // (3,2)
1097                    Complex64::new(cos, 0.0),  // (3,3)
1098                ])
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                // Parameter not found in values, return a clone of the original gate
1170                Ok(Box::new(self.clone()))
1171            }
1172            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
1173        }
1174    }
1175
1176    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1177        self.assign(values)
1178    }
1179}
1180
1181/// Phase shift gate with a parameterized phase
1182#[derive(Debug, Clone)]
1183pub struct ParametricPhaseShift {
1184    /// Target qubit
1185    pub target: QubitId,
1186
1187    /// Phase parameter
1188    pub phi: Parameter,
1189}
1190
1191impl ParametricPhaseShift {
1192    /// Create a new phase shift gate with a constant phase
1193    pub const fn new(target: QubitId, phi: f64) -> Self {
1194        Self {
1195            target,
1196            phi: Parameter::constant(phi),
1197        }
1198    }
1199
1200    /// Create a new phase shift gate with a symbolic phase
1201    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                // Parameter not found in values, return a clone of the original gate
1306                Ok(Box::new(self.clone()))
1307            }
1308            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
1309        }
1310    }
1311
1312    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1313        self.assign(values)
1314    }
1315}
1316
1317/// Module for utilities related to parametric gates
1318pub mod utils {
1319    use super::*;
1320    use crate::gate::{multi, single};
1321
1322    /// Helper function to create a parametric gate from a standard gate
1323    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    /// Convert a parameter value to a symbolic parameter
1346    pub fn symbolize_parameter(param: f64, name: &str) -> Parameter {
1347        Parameter::symbol_with_value(name, param)
1348    }
1349
1350    /// Check if two parameters are approximately equal
1351    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}