quantrs2_core/
parametric.rs

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