quantrs2_core/
parametric.rs

1use num_complex::Complex64;
2use std::f64::consts::PI;
3use std::ops::{Add, Mul, Sub};
4
5use crate::error::QuantRS2Result;
6use crate::gate::GateOp;
7use crate::qubit::QubitId;
8
9/// Parameter value representation for parametric gates
10#[derive(Debug, Clone)]
11pub enum Parameter {
12    /// Constant floating-point value
13    Constant(f64),
14
15    /// Symbolic parameter with a name and optional value
16    Symbol(SymbolicParameter),
17}
18
19impl Parameter {
20    /// Create a new constant parameter
21    pub fn constant(value: f64) -> Self {
22        Parameter::Constant(value)
23    }
24
25    /// Create a new symbolic parameter
26    pub fn symbol(name: &str) -> Self {
27        Parameter::Symbol(SymbolicParameter::new(name))
28    }
29
30    /// Create a new symbolic parameter with a value
31    pub fn symbol_with_value(name: &str, value: f64) -> Self {
32        Parameter::Symbol(SymbolicParameter::with_value(name, value))
33    }
34
35    /// Get the value of the parameter, if available
36    pub fn value(&self) -> Option<f64> {
37        match self {
38            Parameter::Constant(val) => Some(*val),
39            Parameter::Symbol(sym) => sym.value,
40        }
41    }
42
43    /// Check if the parameter has a concrete value
44    pub fn has_value(&self) -> bool {
45        match self {
46            Parameter::Constant(_) => true,
47            Parameter::Symbol(sym) => sym.value.is_some(),
48        }
49    }
50}
51
52impl From<f64> for Parameter {
53    fn from(value: f64) -> Self {
54        Parameter::Constant(value)
55    }
56}
57
58impl Add for Parameter {
59    type Output = Self;
60
61    fn add(self, rhs: Self) -> Self::Output {
62        match (self.clone(), rhs.clone()) {
63            (Parameter::Constant(a), Parameter::Constant(b)) => Parameter::Constant(a + b),
64            // For other cases, we'd need a more complex expression tree - this is simplified
65            _ => {
66                if let (Some(a_val), Some(b_val)) = (self.value(), rhs.value()) {
67                    Parameter::Constant(a_val + b_val)
68                } else {
69                    // Fallback to keeping the left parameter
70                    self
71                }
72            }
73        }
74    }
75}
76
77impl Sub for Parameter {
78    type Output = Self;
79
80    fn sub(self, rhs: Self) -> Self::Output {
81        match (self.clone(), rhs.clone()) {
82            (Parameter::Constant(a), Parameter::Constant(b)) => Parameter::Constant(a - b),
83            // For other cases, we'd need a more complex expression tree - this is simplified
84            _ => {
85                if let (Some(a_val), Some(b_val)) = (self.value(), rhs.value()) {
86                    Parameter::Constant(a_val - b_val)
87                } else {
88                    // Fallback to keeping the left parameter
89                    self
90                }
91            }
92        }
93    }
94}
95
96impl Mul for Parameter {
97    type Output = Self;
98
99    fn mul(self, rhs: Self) -> Self::Output {
100        match (self.clone(), rhs.clone()) {
101            (Parameter::Constant(a), Parameter::Constant(b)) => Parameter::Constant(a * b),
102            // For other cases, we'd need a more complex expression tree - this is simplified
103            _ => {
104                if let (Some(a_val), Some(b_val)) = (self.value(), rhs.value()) {
105                    Parameter::Constant(a_val * b_val)
106                } else {
107                    // Fallback to keeping the left parameter
108                    self
109                }
110            }
111        }
112    }
113}
114
115/// Symbolic parameter that can be used in parametric gates
116#[derive(Debug, Clone)]
117pub struct SymbolicParameter {
118    /// Name of the parameter
119    pub name: String,
120
121    /// Optional value of the parameter
122    pub value: Option<f64>,
123}
124
125impl SymbolicParameter {
126    /// Create a new symbolic parameter without a value
127    pub fn new(name: &str) -> Self {
128        SymbolicParameter {
129            name: name.to_string(),
130            value: None,
131        }
132    }
133
134    /// Create a new symbolic parameter with a value
135    pub fn with_value(name: &str, value: f64) -> Self {
136        SymbolicParameter {
137            name: name.to_string(),
138            value: Some(value),
139        }
140    }
141
142    /// Set the value of the parameter
143    pub fn set_value(&mut self, value: f64) {
144        self.value = Some(value);
145    }
146
147    /// Clear the value of the parameter
148    pub fn clear_value(&mut self) {
149        self.value = None;
150    }
151}
152
153// Note: Cannot implement Copy because String doesn't implement Copy
154// Use Clone instead (already implemented above)
155
156/// Trait for parametric gates that extends GateOp with parameter-related functionality
157pub trait ParametricGate: GateOp {
158    /// Returns the parameters of the gate
159    fn parameters(&self) -> Vec<Parameter>;
160
161    /// Returns the names of the parameters
162    fn parameter_names(&self) -> Vec<String>;
163
164    /// Returns a new gate with updated parameters
165    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>>;
166
167    /// Returns a new gate with updated parameter at the specified index
168    fn with_parameter_at(
169        &self,
170        index: usize,
171        param: Parameter,
172    ) -> QuantRS2Result<Box<dyn ParametricGate>>;
173
174    /// Assigns values to symbolic parameters
175    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
176
177    /// Returns the gate with all parameters set to the specified values
178    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
179}
180
181/// Specialized implementation of rotation gates around the X-axis
182#[derive(Debug, Clone)]
183pub struct ParametricRotationX {
184    /// Target qubit
185    pub target: QubitId,
186
187    /// Rotation angle parameter
188    pub theta: Parameter,
189}
190
191impl ParametricRotationX {
192    /// Create a new X-rotation gate with a constant angle
193    pub fn new(target: QubitId, theta: f64) -> Self {
194        ParametricRotationX {
195            target,
196            theta: Parameter::constant(theta),
197        }
198    }
199
200    /// Create a new X-rotation gate with a symbolic angle
201    pub fn new_symbolic(target: QubitId, name: &str) -> Self {
202        ParametricRotationX {
203            target,
204            theta: Parameter::symbol(name),
205        }
206    }
207}
208
209impl GateOp for ParametricRotationX {
210    fn name(&self) -> &'static str {
211        "RX"
212    }
213
214    fn qubits(&self) -> Vec<QubitId> {
215        vec![self.target]
216    }
217
218    fn is_parameterized(&self) -> bool {
219        true
220    }
221
222    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
223        if let Some(theta) = self.theta.value() {
224            let cos = (theta / 2.0).cos();
225            let sin = (theta / 2.0).sin();
226            Ok(vec![
227                Complex64::new(cos, 0.0),
228                Complex64::new(0.0, -sin),
229                Complex64::new(0.0, -sin),
230                Complex64::new(cos, 0.0),
231            ])
232        } else {
233            Err(crate::error::QuantRS2Error::UnsupportedOperation(
234                "Cannot generate matrix for RX gate with unbound symbolic parameter".into(),
235            ))
236        }
237    }
238
239    fn as_any(&self) -> &dyn std::any::Any {
240        self
241    }
242}
243
244impl ParametricGate for ParametricRotationX {
245    fn parameters(&self) -> Vec<Parameter> {
246        vec![self.theta.clone()]
247    }
248
249    fn parameter_names(&self) -> Vec<String> {
250        match self.theta {
251            Parameter::Symbol(ref sym) => vec![sym.name.clone()],
252            _ => Vec::new(),
253        }
254    }
255
256    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
257        if params.len() != 1 {
258            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
259                "RotationX expects 1 parameter, got {}",
260                params.len()
261            )));
262        }
263        Ok(Box::new(ParametricRotationX {
264            target: self.target,
265            theta: params[0].clone(),
266        }))
267    }
268
269    fn with_parameter_at(
270        &self,
271        index: usize,
272        param: Parameter,
273    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
274        if index != 0 {
275            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
276                "RotationX has only 1 parameter, got index {}",
277                index
278            )));
279        }
280        Ok(Box::new(ParametricRotationX {
281            target: self.target,
282            theta: param.clone(),
283        }))
284    }
285
286    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
287        match self.theta {
288            Parameter::Symbol(ref sym) => {
289                for (name, value) in values {
290                    if sym.name == *name {
291                        return Ok(Box::new(ParametricRotationX {
292                            target: self.target,
293                            theta: Parameter::Symbol(SymbolicParameter::with_value(
294                                &sym.name, *value,
295                            )),
296                        }));
297                    }
298                }
299                // Parameter not found in values, return a clone of the original gate
300                Ok(Box::new(self.clone()))
301            }
302            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
303        }
304    }
305
306    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
307        self.assign(values)
308    }
309}
310
311/// Specialized implementation of rotation gates around the Y-axis
312#[derive(Debug, Clone)]
313pub struct ParametricRotationY {
314    /// Target qubit
315    pub target: QubitId,
316
317    /// Rotation angle parameter
318    pub theta: Parameter,
319}
320
321impl ParametricRotationY {
322    /// Create a new Y-rotation gate with a constant angle
323    pub fn new(target: QubitId, theta: f64) -> Self {
324        ParametricRotationY {
325            target,
326            theta: Parameter::constant(theta),
327        }
328    }
329
330    /// Create a new Y-rotation gate with a symbolic angle
331    pub fn new_symbolic(target: QubitId, name: &str) -> Self {
332        ParametricRotationY {
333            target,
334            theta: Parameter::symbol(name),
335        }
336    }
337}
338
339impl GateOp for ParametricRotationY {
340    fn name(&self) -> &'static str {
341        "RY"
342    }
343
344    fn qubits(&self) -> Vec<QubitId> {
345        vec![self.target]
346    }
347
348    fn is_parameterized(&self) -> bool {
349        true
350    }
351
352    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
353        if let Some(theta) = self.theta.value() {
354            let cos = (theta / 2.0).cos();
355            let sin = (theta / 2.0).sin();
356            Ok(vec![
357                Complex64::new(cos, 0.0),
358                Complex64::new(-sin, 0.0),
359                Complex64::new(sin, 0.0),
360                Complex64::new(cos, 0.0),
361            ])
362        } else {
363            Err(crate::error::QuantRS2Error::UnsupportedOperation(
364                "Cannot generate matrix for RY gate with unbound symbolic parameter".into(),
365            ))
366        }
367    }
368
369    fn as_any(&self) -> &dyn std::any::Any {
370        self
371    }
372}
373
374impl ParametricGate for ParametricRotationY {
375    fn parameters(&self) -> Vec<Parameter> {
376        vec![self.theta.clone()]
377    }
378
379    fn parameter_names(&self) -> Vec<String> {
380        match self.theta {
381            Parameter::Symbol(ref sym) => vec![sym.name.clone()],
382            _ => Vec::new(),
383        }
384    }
385
386    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
387        if params.len() != 1 {
388            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
389                "RotationY expects 1 parameter, got {}",
390                params.len()
391            )));
392        }
393        Ok(Box::new(ParametricRotationY {
394            target: self.target,
395            theta: params[0].clone(),
396        }))
397    }
398
399    fn with_parameter_at(
400        &self,
401        index: usize,
402        param: Parameter,
403    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
404        if index != 0 {
405            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
406                "RotationY has only 1 parameter, got index {}",
407                index
408            )));
409        }
410        Ok(Box::new(ParametricRotationY {
411            target: self.target,
412            theta: param.clone(),
413        }))
414    }
415
416    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
417        match self.theta {
418            Parameter::Symbol(ref sym) => {
419                for (name, value) in values {
420                    if sym.name == *name {
421                        return Ok(Box::new(ParametricRotationY {
422                            target: self.target,
423                            theta: Parameter::Symbol(SymbolicParameter::with_value(
424                                &sym.name, *value,
425                            )),
426                        }));
427                    }
428                }
429                // Parameter not found in values, return a clone of the original gate
430                Ok(Box::new(self.clone()))
431            }
432            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
433        }
434    }
435
436    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
437        self.assign(values)
438    }
439}
440
441/// Specialized implementation of rotation gates around the Z-axis
442#[derive(Debug, Clone)]
443pub struct ParametricRotationZ {
444    /// Target qubit
445    pub target: QubitId,
446
447    /// Rotation angle parameter
448    pub theta: Parameter,
449}
450
451impl ParametricRotationZ {
452    /// Create a new Z-rotation gate with a constant angle
453    pub fn new(target: QubitId, theta: f64) -> Self {
454        ParametricRotationZ {
455            target,
456            theta: Parameter::constant(theta),
457        }
458    }
459
460    /// Create a new Z-rotation gate with a symbolic angle
461    pub fn new_symbolic(target: QubitId, name: &str) -> Self {
462        ParametricRotationZ {
463            target,
464            theta: Parameter::symbol(name),
465        }
466    }
467}
468
469impl GateOp for ParametricRotationZ {
470    fn name(&self) -> &'static str {
471        "RZ"
472    }
473
474    fn qubits(&self) -> Vec<QubitId> {
475        vec![self.target]
476    }
477
478    fn is_parameterized(&self) -> bool {
479        true
480    }
481
482    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
483        if let Some(theta) = self.theta.value() {
484            let phase = Complex64::new(0.0, -theta / 2.0).exp();
485            let phase_conj = Complex64::new(0.0, theta / 2.0).exp();
486            Ok(vec![
487                phase_conj,
488                Complex64::new(0.0, 0.0),
489                Complex64::new(0.0, 0.0),
490                phase,
491            ])
492        } else {
493            Err(crate::error::QuantRS2Error::UnsupportedOperation(
494                "Cannot generate matrix for RZ gate with unbound symbolic parameter".into(),
495            ))
496        }
497    }
498
499    fn as_any(&self) -> &dyn std::any::Any {
500        self
501    }
502}
503
504impl ParametricGate for ParametricRotationZ {
505    fn parameters(&self) -> Vec<Parameter> {
506        vec![self.theta.clone()]
507    }
508
509    fn parameter_names(&self) -> Vec<String> {
510        match self.theta {
511            Parameter::Symbol(ref sym) => vec![sym.name.clone()],
512            _ => Vec::new(),
513        }
514    }
515
516    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
517        if params.len() != 1 {
518            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
519                "RotationZ expects 1 parameter, got {}",
520                params.len()
521            )));
522        }
523        Ok(Box::new(ParametricRotationZ {
524            target: self.target,
525            theta: params[0].clone(),
526        }))
527    }
528
529    fn with_parameter_at(
530        &self,
531        index: usize,
532        param: Parameter,
533    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
534        if index != 0 {
535            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
536                "RotationZ has only 1 parameter, got index {}",
537                index
538            )));
539        }
540        Ok(Box::new(ParametricRotationZ {
541            target: self.target,
542            theta: param.clone(),
543        }))
544    }
545
546    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
547        match self.theta {
548            Parameter::Symbol(ref sym) => {
549                for (name, value) in values {
550                    if sym.name == *name {
551                        return Ok(Box::new(ParametricRotationZ {
552                            target: self.target,
553                            theta: Parameter::Symbol(SymbolicParameter::with_value(
554                                &sym.name, *value,
555                            )),
556                        }));
557                    }
558                }
559                // Parameter not found in values, return a clone of the original gate
560                Ok(Box::new(self.clone()))
561            }
562            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
563        }
564    }
565
566    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
567        self.assign(values)
568    }
569}
570
571/// Specialized implementation of a general U-gate (parameterized single-qubit gate)
572#[derive(Debug, Clone)]
573pub struct ParametricU {
574    /// Target qubit
575    pub target: QubitId,
576
577    /// Theta parameter (rotation angle)
578    pub theta: Parameter,
579
580    /// Phi parameter (phase angle)
581    pub phi: Parameter,
582
583    /// Lambda parameter (phase angle)
584    pub lambda: Parameter,
585}
586
587impl ParametricU {
588    /// Create a new U-gate with constant angles
589    pub fn new(target: QubitId, theta: f64, phi: f64, lambda: f64) -> Self {
590        ParametricU {
591            target,
592            theta: Parameter::constant(theta),
593            phi: Parameter::constant(phi),
594            lambda: Parameter::constant(lambda),
595        }
596    }
597
598    /// Create a new U-gate with symbolic angles
599    pub fn new_symbolic(
600        target: QubitId,
601        theta_name: &str,
602        phi_name: &str,
603        lambda_name: &str,
604    ) -> Self {
605        ParametricU {
606            target,
607            theta: Parameter::symbol(theta_name),
608            phi: Parameter::symbol(phi_name),
609            lambda: Parameter::symbol(lambda_name),
610        }
611    }
612}
613
614impl GateOp for ParametricU {
615    fn name(&self) -> &'static str {
616        "U"
617    }
618
619    fn qubits(&self) -> Vec<QubitId> {
620        vec![self.target]
621    }
622
623    fn is_parameterized(&self) -> bool {
624        true
625    }
626
627    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
628        if let (Some(theta), Some(phi), Some(lambda)) =
629            (self.theta.value(), self.phi.value(), self.lambda.value())
630        {
631            let cos = (theta / 2.0).cos();
632            let sin = (theta / 2.0).sin();
633
634            let e_phi = Complex64::new(0.0, phi).exp();
635            let e_lambda = Complex64::new(0.0, lambda).exp();
636
637            Ok(vec![
638                Complex64::new(cos, 0.0),
639                -sin * e_lambda,
640                sin * e_phi,
641                cos * e_phi * e_lambda,
642            ])
643        } else {
644            Err(crate::error::QuantRS2Error::UnsupportedOperation(
645                "Cannot generate matrix for U gate with unbound symbolic parameters".into(),
646            ))
647        }
648    }
649
650    fn as_any(&self) -> &dyn std::any::Any {
651        self
652    }
653}
654
655impl ParametricGate for ParametricU {
656    fn parameters(&self) -> Vec<Parameter> {
657        vec![self.theta.clone(), self.phi.clone(), self.lambda.clone()]
658    }
659
660    fn parameter_names(&self) -> Vec<String> {
661        let mut names = Vec::new();
662
663        if let Parameter::Symbol(ref sym) = self.theta {
664            names.push(sym.name.clone());
665        }
666
667        if let Parameter::Symbol(ref sym) = self.phi {
668            names.push(sym.name.clone());
669        }
670
671        if let Parameter::Symbol(ref sym) = self.lambda {
672            names.push(sym.name.clone());
673        }
674
675        names
676    }
677
678    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
679        if params.len() != 3 {
680            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
681                "U gate expects 3 parameters, got {}",
682                params.len()
683            )));
684        }
685        Ok(Box::new(ParametricU {
686            target: self.target,
687            theta: params[0].clone(),
688            phi: params[1].clone(),
689            lambda: params[2].clone(),
690        }))
691    }
692
693    fn with_parameter_at(
694        &self,
695        index: usize,
696        param: Parameter,
697    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
698        match index {
699            0 => Ok(Box::new(ParametricU {
700                target: self.target,
701                theta: param.clone(),
702                phi: self.phi.clone(),
703                lambda: self.lambda.clone(),
704            })),
705            1 => Ok(Box::new(ParametricU {
706                target: self.target,
707                theta: self.theta.clone(),
708                phi: param.clone(),
709                lambda: self.lambda.clone(),
710            })),
711            2 => Ok(Box::new(ParametricU {
712                target: self.target,
713                theta: self.theta.clone(),
714                phi: self.phi.clone(),
715                lambda: param.clone(),
716            })),
717            _ => Err(crate::error::QuantRS2Error::InvalidInput(format!(
718                "U gate has only 3 parameters, got index {}",
719                index
720            ))),
721        }
722    }
723
724    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
725        let mut result = self.clone();
726
727        // Update theta if it's a symbolic parameter
728        if let Parameter::Symbol(ref sym) = self.theta {
729            for (name, value) in values {
730                if sym.name == *name {
731                    result.theta =
732                        Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
733                    break;
734                }
735            }
736        }
737
738        // Update phi if it's a symbolic parameter
739        if let Parameter::Symbol(ref sym) = self.phi {
740            for (name, value) in values {
741                if sym.name == *name {
742                    result.phi =
743                        Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
744                    break;
745                }
746            }
747        }
748
749        // Update lambda if it's a symbolic parameter
750        if let Parameter::Symbol(ref sym) = self.lambda {
751            for (name, value) in values {
752                if sym.name == *name {
753                    result.lambda =
754                        Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
755                    break;
756                }
757            }
758        }
759
760        Ok(Box::new(result))
761    }
762
763    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
764        self.assign(values)
765    }
766}
767
768/// Specialized implementation of controlled parametric rotation around X-axis
769#[derive(Debug, Clone)]
770pub struct ParametricCRX {
771    /// Control qubit
772    pub control: QubitId,
773
774    /// Target qubit
775    pub target: QubitId,
776
777    /// Rotation angle parameter
778    pub theta: Parameter,
779}
780
781impl ParametricCRX {
782    /// Create a new CRX gate with a constant angle
783    pub fn new(control: QubitId, target: QubitId, theta: f64) -> Self {
784        ParametricCRX {
785            control,
786            target,
787            theta: Parameter::constant(theta),
788        }
789    }
790
791    /// Create a new CRX gate with a symbolic angle
792    pub fn new_symbolic(control: QubitId, target: QubitId, name: &str) -> Self {
793        ParametricCRX {
794            control,
795            target,
796            theta: Parameter::symbol(name),
797        }
798    }
799}
800
801impl GateOp for ParametricCRX {
802    fn name(&self) -> &'static str {
803        "CRX"
804    }
805
806    fn qubits(&self) -> Vec<QubitId> {
807        vec![self.control, self.target]
808    }
809
810    fn is_parameterized(&self) -> bool {
811        true
812    }
813
814    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
815        if let Some(theta) = self.theta.value() {
816            let cos = (theta / 2.0).cos();
817            let sin = (theta / 2.0).sin();
818
819            Ok(vec![
820                Complex64::new(1.0, 0.0),  // (0,0)
821                Complex64::new(0.0, 0.0),  // (0,1)
822                Complex64::new(0.0, 0.0),  // (0,2)
823                Complex64::new(0.0, 0.0),  // (0,3)
824                Complex64::new(0.0, 0.0),  // (1,0)
825                Complex64::new(1.0, 0.0),  // (1,1)
826                Complex64::new(0.0, 0.0),  // (1,2)
827                Complex64::new(0.0, 0.0),  // (1,3)
828                Complex64::new(0.0, 0.0),  // (2,0)
829                Complex64::new(0.0, 0.0),  // (2,1)
830                Complex64::new(cos, 0.0),  // (2,2)
831                Complex64::new(0.0, -sin), // (2,3)
832                Complex64::new(0.0, 0.0),  // (3,0)
833                Complex64::new(0.0, 0.0),  // (3,1)
834                Complex64::new(0.0, -sin), // (3,2)
835                Complex64::new(cos, 0.0),  // (3,3)
836            ])
837        } else {
838            Err(crate::error::QuantRS2Error::UnsupportedOperation(
839                "Cannot generate matrix for CRX gate with unbound symbolic parameter".into(),
840            ))
841        }
842    }
843
844    fn as_any(&self) -> &dyn std::any::Any {
845        self
846    }
847}
848
849impl ParametricGate for ParametricCRX {
850    fn parameters(&self) -> Vec<Parameter> {
851        vec![self.theta.clone()]
852    }
853
854    fn parameter_names(&self) -> Vec<String> {
855        match self.theta {
856            Parameter::Symbol(ref sym) => vec![sym.name.clone()],
857            _ => Vec::new(),
858        }
859    }
860
861    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
862        if params.len() != 1 {
863            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
864                "CRX expects 1 parameter, got {}",
865                params.len()
866            )));
867        }
868        Ok(Box::new(ParametricCRX {
869            control: self.control,
870            target: self.target,
871            theta: params[0].clone(),
872        }))
873    }
874
875    fn with_parameter_at(
876        &self,
877        index: usize,
878        param: Parameter,
879    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
880        if index != 0 {
881            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
882                "CRX has only 1 parameter, got index {}",
883                index
884            )));
885        }
886        Ok(Box::new(ParametricCRX {
887            control: self.control,
888            target: self.target,
889            theta: param.clone(),
890        }))
891    }
892
893    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
894        match self.theta {
895            Parameter::Symbol(ref sym) => {
896                for (name, value) in values {
897                    if sym.name == *name {
898                        return Ok(Box::new(ParametricCRX {
899                            control: self.control,
900                            target: self.target,
901                            theta: Parameter::Symbol(SymbolicParameter::with_value(
902                                &sym.name, *value,
903                            )),
904                        }));
905                    }
906                }
907                // Parameter not found in values, return a clone of the original gate
908                Ok(Box::new(self.clone()))
909            }
910            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
911        }
912    }
913
914    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
915        self.assign(values)
916    }
917}
918
919/// Phase shift gate with a parameterized phase
920#[derive(Debug, Clone)]
921pub struct ParametricPhaseShift {
922    /// Target qubit
923    pub target: QubitId,
924
925    /// Phase parameter
926    pub phi: Parameter,
927}
928
929impl ParametricPhaseShift {
930    /// Create a new phase shift gate with a constant phase
931    pub fn new(target: QubitId, phi: f64) -> Self {
932        ParametricPhaseShift {
933            target,
934            phi: Parameter::constant(phi),
935        }
936    }
937
938    /// Create a new phase shift gate with a symbolic phase
939    pub fn new_symbolic(target: QubitId, name: &str) -> Self {
940        ParametricPhaseShift {
941            target,
942            phi: Parameter::symbol(name),
943        }
944    }
945}
946
947impl GateOp for ParametricPhaseShift {
948    fn name(&self) -> &'static str {
949        "P"
950    }
951
952    fn qubits(&self) -> Vec<QubitId> {
953        vec![self.target]
954    }
955
956    fn is_parameterized(&self) -> bool {
957        true
958    }
959
960    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
961        if let Some(phi) = self.phi.value() {
962            let phase = Complex64::new(phi.cos(), phi.sin());
963            Ok(vec![
964                Complex64::new(1.0, 0.0),
965                Complex64::new(0.0, 0.0),
966                Complex64::new(0.0, 0.0),
967                phase,
968            ])
969        } else {
970            Err(crate::error::QuantRS2Error::UnsupportedOperation(
971                "Cannot generate matrix for phase shift gate with unbound symbolic parameter"
972                    .into(),
973            ))
974        }
975    }
976
977    fn as_any(&self) -> &dyn std::any::Any {
978        self
979    }
980}
981
982impl ParametricGate for ParametricPhaseShift {
983    fn parameters(&self) -> Vec<Parameter> {
984        vec![self.phi.clone()]
985    }
986
987    fn parameter_names(&self) -> Vec<String> {
988        match self.phi {
989            Parameter::Symbol(ref sym) => vec![sym.name.clone()],
990            _ => Vec::new(),
991        }
992    }
993
994    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
995        if params.len() != 1 {
996            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
997                "Phase shift gate expects 1 parameter, got {}",
998                params.len()
999            )));
1000        }
1001        Ok(Box::new(ParametricPhaseShift {
1002            target: self.target,
1003            phi: params[0].clone(),
1004        }))
1005    }
1006
1007    fn with_parameter_at(
1008        &self,
1009        index: usize,
1010        param: Parameter,
1011    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
1012        if index != 0 {
1013            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
1014                "Phase shift gate has only 1 parameter, got index {}",
1015                index
1016            )));
1017        }
1018        Ok(Box::new(ParametricPhaseShift {
1019            target: self.target,
1020            phi: param.clone(),
1021        }))
1022    }
1023
1024    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1025        match self.phi {
1026            Parameter::Symbol(ref sym) => {
1027                for (name, value) in values {
1028                    if sym.name == *name {
1029                        return Ok(Box::new(ParametricPhaseShift {
1030                            target: self.target,
1031                            phi: Parameter::Symbol(SymbolicParameter::with_value(
1032                                &sym.name, *value,
1033                            )),
1034                        }));
1035                    }
1036                }
1037                // Parameter not found in values, return a clone of the original gate
1038                Ok(Box::new(self.clone()))
1039            }
1040            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
1041        }
1042    }
1043
1044    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1045        self.assign(values)
1046    }
1047}
1048
1049/// Module for utilities related to parametric gates
1050pub mod utils {
1051    use super::*;
1052    use crate::gate::{multi, single};
1053
1054    /// Helper function to create a parametric gate from a standard gate
1055    pub fn parametrize_rotation_gate(gate: &dyn GateOp) -> Option<Box<dyn ParametricGate>> {
1056        if !gate.is_parameterized() {
1057            return None;
1058        }
1059
1060        if let Some(rx) = gate.as_any().downcast_ref::<single::RotationX>() {
1061            Some(Box::new(ParametricRotationX::new(rx.target, rx.theta)))
1062        } else if let Some(ry) = gate.as_any().downcast_ref::<single::RotationY>() {
1063            Some(Box::new(ParametricRotationY::new(ry.target, ry.theta)))
1064        } else if let Some(rz) = gate.as_any().downcast_ref::<single::RotationZ>() {
1065            Some(Box::new(ParametricRotationZ::new(rz.target, rz.theta)))
1066        } else if let Some(crx) = gate.as_any().downcast_ref::<multi::CRX>() {
1067            Some(Box::new(ParametricCRX::new(
1068                crx.control,
1069                crx.target,
1070                crx.theta,
1071            )))
1072        } else {
1073            None
1074        }
1075    }
1076
1077    /// Convert a parameter value to a symbolic parameter
1078    pub fn symbolize_parameter(param: f64, name: &str) -> Parameter {
1079        Parameter::symbol_with_value(name, param)
1080    }
1081
1082    /// Check if two parameters are approximately equal
1083    pub fn parameters_approx_eq(p1: &Parameter, p2: &Parameter, epsilon: f64) -> bool {
1084        match (p1.value(), p2.value()) {
1085            (Some(v1), Some(v2)) => (v1 - v2).abs() < epsilon,
1086            _ => false,
1087        }
1088    }
1089}