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    fn clone_gate(&self) -> Box<dyn GateOp> {
243        Box::new(self.clone())
244    }
245}
246
247impl ParametricGate for ParametricRotationX {
248    fn parameters(&self) -> Vec<Parameter> {
249        vec![self.theta.clone()]
250    }
251
252    fn parameter_names(&self) -> Vec<String> {
253        match self.theta {
254            Parameter::Symbol(ref sym) => vec![sym.name.clone()],
255            _ => Vec::new(),
256        }
257    }
258
259    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
260        if params.len() != 1 {
261            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
262                "RotationX expects 1 parameter, got {}",
263                params.len()
264            )));
265        }
266        Ok(Box::new(ParametricRotationX {
267            target: self.target,
268            theta: params[0].clone(),
269        }))
270    }
271
272    fn with_parameter_at(
273        &self,
274        index: usize,
275        param: Parameter,
276    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
277        if index != 0 {
278            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
279                "RotationX has only 1 parameter, got index {}",
280                index
281            )));
282        }
283        Ok(Box::new(ParametricRotationX {
284            target: self.target,
285            theta: param.clone(),
286        }))
287    }
288
289    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
290        match self.theta {
291            Parameter::Symbol(ref sym) => {
292                for (name, value) in values {
293                    if sym.name == *name {
294                        return Ok(Box::new(ParametricRotationX {
295                            target: self.target,
296                            theta: Parameter::Symbol(SymbolicParameter::with_value(
297                                &sym.name, *value,
298                            )),
299                        }));
300                    }
301                }
302                // Parameter not found in values, return a clone of the original gate
303                Ok(Box::new(self.clone()))
304            }
305            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
306        }
307    }
308
309    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
310        self.assign(values)
311    }
312}
313
314/// Specialized implementation of rotation gates around the Y-axis
315#[derive(Debug, Clone)]
316pub struct ParametricRotationY {
317    /// Target qubit
318    pub target: QubitId,
319
320    /// Rotation angle parameter
321    pub theta: Parameter,
322}
323
324impl ParametricRotationY {
325    /// Create a new Y-rotation gate with a constant angle
326    pub fn new(target: QubitId, theta: f64) -> Self {
327        ParametricRotationY {
328            target,
329            theta: Parameter::constant(theta),
330        }
331    }
332
333    /// Create a new Y-rotation gate with a symbolic angle
334    pub fn new_symbolic(target: QubitId, name: &str) -> Self {
335        ParametricRotationY {
336            target,
337            theta: Parameter::symbol(name),
338        }
339    }
340}
341
342impl GateOp for ParametricRotationY {
343    fn name(&self) -> &'static str {
344        "RY"
345    }
346
347    fn qubits(&self) -> Vec<QubitId> {
348        vec![self.target]
349    }
350
351    fn is_parameterized(&self) -> bool {
352        true
353    }
354
355    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
356        if let Some(theta) = self.theta.value() {
357            let cos = (theta / 2.0).cos();
358            let sin = (theta / 2.0).sin();
359            Ok(vec![
360                Complex64::new(cos, 0.0),
361                Complex64::new(-sin, 0.0),
362                Complex64::new(sin, 0.0),
363                Complex64::new(cos, 0.0),
364            ])
365        } else {
366            Err(crate::error::QuantRS2Error::UnsupportedOperation(
367                "Cannot generate matrix for RY gate with unbound symbolic parameter".into(),
368            ))
369        }
370    }
371
372    fn as_any(&self) -> &dyn std::any::Any {
373        self
374    }
375
376    fn clone_gate(&self) -> Box<dyn GateOp> {
377        Box::new(self.clone())
378    }
379}
380
381impl ParametricGate for ParametricRotationY {
382    fn parameters(&self) -> Vec<Parameter> {
383        vec![self.theta.clone()]
384    }
385
386    fn parameter_names(&self) -> Vec<String> {
387        match self.theta {
388            Parameter::Symbol(ref sym) => vec![sym.name.clone()],
389            _ => Vec::new(),
390        }
391    }
392
393    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
394        if params.len() != 1 {
395            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
396                "RotationY expects 1 parameter, got {}",
397                params.len()
398            )));
399        }
400        Ok(Box::new(ParametricRotationY {
401            target: self.target,
402            theta: params[0].clone(),
403        }))
404    }
405
406    fn with_parameter_at(
407        &self,
408        index: usize,
409        param: Parameter,
410    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
411        if index != 0 {
412            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
413                "RotationY has only 1 parameter, got index {}",
414                index
415            )));
416        }
417        Ok(Box::new(ParametricRotationY {
418            target: self.target,
419            theta: param.clone(),
420        }))
421    }
422
423    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
424        match self.theta {
425            Parameter::Symbol(ref sym) => {
426                for (name, value) in values {
427                    if sym.name == *name {
428                        return Ok(Box::new(ParametricRotationY {
429                            target: self.target,
430                            theta: Parameter::Symbol(SymbolicParameter::with_value(
431                                &sym.name, *value,
432                            )),
433                        }));
434                    }
435                }
436                // Parameter not found in values, return a clone of the original gate
437                Ok(Box::new(self.clone()))
438            }
439            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
440        }
441    }
442
443    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
444        self.assign(values)
445    }
446}
447
448/// Specialized implementation of rotation gates around the Z-axis
449#[derive(Debug, Clone)]
450pub struct ParametricRotationZ {
451    /// Target qubit
452    pub target: QubitId,
453
454    /// Rotation angle parameter
455    pub theta: Parameter,
456}
457
458impl ParametricRotationZ {
459    /// Create a new Z-rotation gate with a constant angle
460    pub fn new(target: QubitId, theta: f64) -> Self {
461        ParametricRotationZ {
462            target,
463            theta: Parameter::constant(theta),
464        }
465    }
466
467    /// Create a new Z-rotation gate with a symbolic angle
468    pub fn new_symbolic(target: QubitId, name: &str) -> Self {
469        ParametricRotationZ {
470            target,
471            theta: Parameter::symbol(name),
472        }
473    }
474}
475
476impl GateOp for ParametricRotationZ {
477    fn name(&self) -> &'static str {
478        "RZ"
479    }
480
481    fn qubits(&self) -> Vec<QubitId> {
482        vec![self.target]
483    }
484
485    fn is_parameterized(&self) -> bool {
486        true
487    }
488
489    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
490        if let Some(theta) = self.theta.value() {
491            let phase = Complex64::new(0.0, -theta / 2.0).exp();
492            let phase_conj = Complex64::new(0.0, theta / 2.0).exp();
493            Ok(vec![
494                phase_conj,
495                Complex64::new(0.0, 0.0),
496                Complex64::new(0.0, 0.0),
497                phase,
498            ])
499        } else {
500            Err(crate::error::QuantRS2Error::UnsupportedOperation(
501                "Cannot generate matrix for RZ gate with unbound symbolic parameter".into(),
502            ))
503        }
504    }
505
506    fn as_any(&self) -> &dyn std::any::Any {
507        self
508    }
509
510    fn clone_gate(&self) -> Box<dyn GateOp> {
511        Box::new(self.clone())
512    }
513}
514
515impl ParametricGate for ParametricRotationZ {
516    fn parameters(&self) -> Vec<Parameter> {
517        vec![self.theta.clone()]
518    }
519
520    fn parameter_names(&self) -> Vec<String> {
521        match self.theta {
522            Parameter::Symbol(ref sym) => vec![sym.name.clone()],
523            _ => Vec::new(),
524        }
525    }
526
527    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
528        if params.len() != 1 {
529            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
530                "RotationZ expects 1 parameter, got {}",
531                params.len()
532            )));
533        }
534        Ok(Box::new(ParametricRotationZ {
535            target: self.target,
536            theta: params[0].clone(),
537        }))
538    }
539
540    fn with_parameter_at(
541        &self,
542        index: usize,
543        param: Parameter,
544    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
545        if index != 0 {
546            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
547                "RotationZ has only 1 parameter, got index {}",
548                index
549            )));
550        }
551        Ok(Box::new(ParametricRotationZ {
552            target: self.target,
553            theta: param.clone(),
554        }))
555    }
556
557    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
558        match self.theta {
559            Parameter::Symbol(ref sym) => {
560                for (name, value) in values {
561                    if sym.name == *name {
562                        return Ok(Box::new(ParametricRotationZ {
563                            target: self.target,
564                            theta: Parameter::Symbol(SymbolicParameter::with_value(
565                                &sym.name, *value,
566                            )),
567                        }));
568                    }
569                }
570                // Parameter not found in values, return a clone of the original gate
571                Ok(Box::new(self.clone()))
572            }
573            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
574        }
575    }
576
577    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
578        self.assign(values)
579    }
580}
581
582/// Specialized implementation of a general U-gate (parameterized single-qubit gate)
583#[derive(Debug, Clone)]
584pub struct ParametricU {
585    /// Target qubit
586    pub target: QubitId,
587
588    /// Theta parameter (rotation angle)
589    pub theta: Parameter,
590
591    /// Phi parameter (phase angle)
592    pub phi: Parameter,
593
594    /// Lambda parameter (phase angle)
595    pub lambda: Parameter,
596}
597
598impl ParametricU {
599    /// Create a new U-gate with constant angles
600    pub fn new(target: QubitId, theta: f64, phi: f64, lambda: f64) -> Self {
601        ParametricU {
602            target,
603            theta: Parameter::constant(theta),
604            phi: Parameter::constant(phi),
605            lambda: Parameter::constant(lambda),
606        }
607    }
608
609    /// Create a new U-gate with symbolic angles
610    pub fn new_symbolic(
611        target: QubitId,
612        theta_name: &str,
613        phi_name: &str,
614        lambda_name: &str,
615    ) -> Self {
616        ParametricU {
617            target,
618            theta: Parameter::symbol(theta_name),
619            phi: Parameter::symbol(phi_name),
620            lambda: Parameter::symbol(lambda_name),
621        }
622    }
623}
624
625impl GateOp for ParametricU {
626    fn name(&self) -> &'static str {
627        "U"
628    }
629
630    fn qubits(&self) -> Vec<QubitId> {
631        vec![self.target]
632    }
633
634    fn is_parameterized(&self) -> bool {
635        true
636    }
637
638    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
639        if let (Some(theta), Some(phi), Some(lambda)) =
640            (self.theta.value(), self.phi.value(), self.lambda.value())
641        {
642            let cos = (theta / 2.0).cos();
643            let sin = (theta / 2.0).sin();
644
645            let e_phi = Complex64::new(0.0, phi).exp();
646            let e_lambda = Complex64::new(0.0, lambda).exp();
647
648            Ok(vec![
649                Complex64::new(cos, 0.0),
650                -sin * e_lambda,
651                sin * e_phi,
652                cos * e_phi * e_lambda,
653            ])
654        } else {
655            Err(crate::error::QuantRS2Error::UnsupportedOperation(
656                "Cannot generate matrix for U gate with unbound symbolic parameters".into(),
657            ))
658        }
659    }
660
661    fn as_any(&self) -> &dyn std::any::Any {
662        self
663    }
664
665    fn clone_gate(&self) -> Box<dyn GateOp> {
666        Box::new(self.clone())
667    }
668}
669
670impl ParametricGate for ParametricU {
671    fn parameters(&self) -> Vec<Parameter> {
672        vec![self.theta.clone(), self.phi.clone(), self.lambda.clone()]
673    }
674
675    fn parameter_names(&self) -> Vec<String> {
676        let mut names = Vec::new();
677
678        if let Parameter::Symbol(ref sym) = self.theta {
679            names.push(sym.name.clone());
680        }
681
682        if let Parameter::Symbol(ref sym) = self.phi {
683            names.push(sym.name.clone());
684        }
685
686        if let Parameter::Symbol(ref sym) = self.lambda {
687            names.push(sym.name.clone());
688        }
689
690        names
691    }
692
693    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
694        if params.len() != 3 {
695            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
696                "U gate expects 3 parameters, got {}",
697                params.len()
698            )));
699        }
700        Ok(Box::new(ParametricU {
701            target: self.target,
702            theta: params[0].clone(),
703            phi: params[1].clone(),
704            lambda: params[2].clone(),
705        }))
706    }
707
708    fn with_parameter_at(
709        &self,
710        index: usize,
711        param: Parameter,
712    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
713        match index {
714            0 => Ok(Box::new(ParametricU {
715                target: self.target,
716                theta: param.clone(),
717                phi: self.phi.clone(),
718                lambda: self.lambda.clone(),
719            })),
720            1 => Ok(Box::new(ParametricU {
721                target: self.target,
722                theta: self.theta.clone(),
723                phi: param.clone(),
724                lambda: self.lambda.clone(),
725            })),
726            2 => Ok(Box::new(ParametricU {
727                target: self.target,
728                theta: self.theta.clone(),
729                phi: self.phi.clone(),
730                lambda: param.clone(),
731            })),
732            _ => Err(crate::error::QuantRS2Error::InvalidInput(format!(
733                "U gate has only 3 parameters, got index {}",
734                index
735            ))),
736        }
737    }
738
739    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
740        let mut result = self.clone();
741
742        // Update theta if it's a symbolic parameter
743        if let Parameter::Symbol(ref sym) = self.theta {
744            for (name, value) in values {
745                if sym.name == *name {
746                    result.theta =
747                        Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
748                    break;
749                }
750            }
751        }
752
753        // Update phi if it's a symbolic parameter
754        if let Parameter::Symbol(ref sym) = self.phi {
755            for (name, value) in values {
756                if sym.name == *name {
757                    result.phi =
758                        Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
759                    break;
760                }
761            }
762        }
763
764        // Update lambda if it's a symbolic parameter
765        if let Parameter::Symbol(ref sym) = self.lambda {
766            for (name, value) in values {
767                if sym.name == *name {
768                    result.lambda =
769                        Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
770                    break;
771                }
772            }
773        }
774
775        Ok(Box::new(result))
776    }
777
778    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
779        self.assign(values)
780    }
781}
782
783/// Specialized implementation of controlled parametric rotation around X-axis
784#[derive(Debug, Clone)]
785pub struct ParametricCRX {
786    /// Control qubit
787    pub control: QubitId,
788
789    /// Target qubit
790    pub target: QubitId,
791
792    /// Rotation angle parameter
793    pub theta: Parameter,
794}
795
796impl ParametricCRX {
797    /// Create a new CRX gate with a constant angle
798    pub fn new(control: QubitId, target: QubitId, theta: f64) -> Self {
799        ParametricCRX {
800            control,
801            target,
802            theta: Parameter::constant(theta),
803        }
804    }
805
806    /// Create a new CRX gate with a symbolic angle
807    pub fn new_symbolic(control: QubitId, target: QubitId, name: &str) -> Self {
808        ParametricCRX {
809            control,
810            target,
811            theta: Parameter::symbol(name),
812        }
813    }
814}
815
816impl GateOp for ParametricCRX {
817    fn name(&self) -> &'static str {
818        "CRX"
819    }
820
821    fn qubits(&self) -> Vec<QubitId> {
822        vec![self.control, self.target]
823    }
824
825    fn is_parameterized(&self) -> bool {
826        true
827    }
828
829    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
830        if let Some(theta) = self.theta.value() {
831            let cos = (theta / 2.0).cos();
832            let sin = (theta / 2.0).sin();
833
834            Ok(vec![
835                Complex64::new(1.0, 0.0),  // (0,0)
836                Complex64::new(0.0, 0.0),  // (0,1)
837                Complex64::new(0.0, 0.0),  // (0,2)
838                Complex64::new(0.0, 0.0),  // (0,3)
839                Complex64::new(0.0, 0.0),  // (1,0)
840                Complex64::new(1.0, 0.0),  // (1,1)
841                Complex64::new(0.0, 0.0),  // (1,2)
842                Complex64::new(0.0, 0.0),  // (1,3)
843                Complex64::new(0.0, 0.0),  // (2,0)
844                Complex64::new(0.0, 0.0),  // (2,1)
845                Complex64::new(cos, 0.0),  // (2,2)
846                Complex64::new(0.0, -sin), // (2,3)
847                Complex64::new(0.0, 0.0),  // (3,0)
848                Complex64::new(0.0, 0.0),  // (3,1)
849                Complex64::new(0.0, -sin), // (3,2)
850                Complex64::new(cos, 0.0),  // (3,3)
851            ])
852        } else {
853            Err(crate::error::QuantRS2Error::UnsupportedOperation(
854                "Cannot generate matrix for CRX gate with unbound symbolic parameter".into(),
855            ))
856        }
857    }
858
859    fn as_any(&self) -> &dyn std::any::Any {
860        self
861    }
862
863    fn clone_gate(&self) -> Box<dyn GateOp> {
864        Box::new(self.clone())
865    }
866}
867
868impl ParametricGate for ParametricCRX {
869    fn parameters(&self) -> Vec<Parameter> {
870        vec![self.theta.clone()]
871    }
872
873    fn parameter_names(&self) -> Vec<String> {
874        match self.theta {
875            Parameter::Symbol(ref sym) => vec![sym.name.clone()],
876            _ => Vec::new(),
877        }
878    }
879
880    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
881        if params.len() != 1 {
882            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
883                "CRX expects 1 parameter, got {}",
884                params.len()
885            )));
886        }
887        Ok(Box::new(ParametricCRX {
888            control: self.control,
889            target: self.target,
890            theta: params[0].clone(),
891        }))
892    }
893
894    fn with_parameter_at(
895        &self,
896        index: usize,
897        param: Parameter,
898    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
899        if index != 0 {
900            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
901                "CRX has only 1 parameter, got index {}",
902                index
903            )));
904        }
905        Ok(Box::new(ParametricCRX {
906            control: self.control,
907            target: self.target,
908            theta: param.clone(),
909        }))
910    }
911
912    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
913        match self.theta {
914            Parameter::Symbol(ref sym) => {
915                for (name, value) in values {
916                    if sym.name == *name {
917                        return Ok(Box::new(ParametricCRX {
918                            control: self.control,
919                            target: self.target,
920                            theta: Parameter::Symbol(SymbolicParameter::with_value(
921                                &sym.name, *value,
922                            )),
923                        }));
924                    }
925                }
926                // Parameter not found in values, return a clone of the original gate
927                Ok(Box::new(self.clone()))
928            }
929            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
930        }
931    }
932
933    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
934        self.assign(values)
935    }
936}
937
938/// Phase shift gate with a parameterized phase
939#[derive(Debug, Clone)]
940pub struct ParametricPhaseShift {
941    /// Target qubit
942    pub target: QubitId,
943
944    /// Phase parameter
945    pub phi: Parameter,
946}
947
948impl ParametricPhaseShift {
949    /// Create a new phase shift gate with a constant phase
950    pub fn new(target: QubitId, phi: f64) -> Self {
951        ParametricPhaseShift {
952            target,
953            phi: Parameter::constant(phi),
954        }
955    }
956
957    /// Create a new phase shift gate with a symbolic phase
958    pub fn new_symbolic(target: QubitId, name: &str) -> Self {
959        ParametricPhaseShift {
960            target,
961            phi: Parameter::symbol(name),
962        }
963    }
964}
965
966impl GateOp for ParametricPhaseShift {
967    fn name(&self) -> &'static str {
968        "P"
969    }
970
971    fn qubits(&self) -> Vec<QubitId> {
972        vec![self.target]
973    }
974
975    fn is_parameterized(&self) -> bool {
976        true
977    }
978
979    fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
980        if let Some(phi) = self.phi.value() {
981            let phase = Complex64::new(phi.cos(), phi.sin());
982            Ok(vec![
983                Complex64::new(1.0, 0.0),
984                Complex64::new(0.0, 0.0),
985                Complex64::new(0.0, 0.0),
986                phase,
987            ])
988        } else {
989            Err(crate::error::QuantRS2Error::UnsupportedOperation(
990                "Cannot generate matrix for phase shift gate with unbound symbolic parameter"
991                    .into(),
992            ))
993        }
994    }
995
996    fn as_any(&self) -> &dyn std::any::Any {
997        self
998    }
999
1000    fn clone_gate(&self) -> Box<dyn GateOp> {
1001        Box::new(self.clone())
1002    }
1003}
1004
1005impl ParametricGate for ParametricPhaseShift {
1006    fn parameters(&self) -> Vec<Parameter> {
1007        vec![self.phi.clone()]
1008    }
1009
1010    fn parameter_names(&self) -> Vec<String> {
1011        match self.phi {
1012            Parameter::Symbol(ref sym) => vec![sym.name.clone()],
1013            _ => Vec::new(),
1014        }
1015    }
1016
1017    fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1018        if params.len() != 1 {
1019            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
1020                "Phase shift gate expects 1 parameter, got {}",
1021                params.len()
1022            )));
1023        }
1024        Ok(Box::new(ParametricPhaseShift {
1025            target: self.target,
1026            phi: params[0].clone(),
1027        }))
1028    }
1029
1030    fn with_parameter_at(
1031        &self,
1032        index: usize,
1033        param: Parameter,
1034    ) -> QuantRS2Result<Box<dyn ParametricGate>> {
1035        if index != 0 {
1036            return Err(crate::error::QuantRS2Error::InvalidInput(format!(
1037                "Phase shift gate has only 1 parameter, got index {}",
1038                index
1039            )));
1040        }
1041        Ok(Box::new(ParametricPhaseShift {
1042            target: self.target,
1043            phi: param.clone(),
1044        }))
1045    }
1046
1047    fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1048        match self.phi {
1049            Parameter::Symbol(ref sym) => {
1050                for (name, value) in values {
1051                    if sym.name == *name {
1052                        return Ok(Box::new(ParametricPhaseShift {
1053                            target: self.target,
1054                            phi: Parameter::Symbol(SymbolicParameter::with_value(
1055                                &sym.name, *value,
1056                            )),
1057                        }));
1058                    }
1059                }
1060                // Parameter not found in values, return a clone of the original gate
1061                Ok(Box::new(self.clone()))
1062            }
1063            _ => Ok(Box::new(self.clone())), // Not a symbolic parameter, no change needed
1064        }
1065    }
1066
1067    fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
1068        self.assign(values)
1069    }
1070}
1071
1072/// Module for utilities related to parametric gates
1073pub mod utils {
1074    use super::*;
1075    use crate::gate::{multi, single};
1076
1077    /// Helper function to create a parametric gate from a standard gate
1078    pub fn parametrize_rotation_gate(gate: &dyn GateOp) -> Option<Box<dyn ParametricGate>> {
1079        if !gate.is_parameterized() {
1080            return None;
1081        }
1082
1083        if let Some(rx) = gate.as_any().downcast_ref::<single::RotationX>() {
1084            Some(Box::new(ParametricRotationX::new(rx.target, rx.theta)))
1085        } else if let Some(ry) = gate.as_any().downcast_ref::<single::RotationY>() {
1086            Some(Box::new(ParametricRotationY::new(ry.target, ry.theta)))
1087        } else if let Some(rz) = gate.as_any().downcast_ref::<single::RotationZ>() {
1088            Some(Box::new(ParametricRotationZ::new(rz.target, rz.theta)))
1089        } else if let Some(crx) = gate.as_any().downcast_ref::<multi::CRX>() {
1090            Some(Box::new(ParametricCRX::new(
1091                crx.control,
1092                crx.target,
1093                crx.theta,
1094            )))
1095        } else {
1096            None
1097        }
1098    }
1099
1100    /// Convert a parameter value to a symbolic parameter
1101    pub fn symbolize_parameter(param: f64, name: &str) -> Parameter {
1102        Parameter::symbol_with_value(name, param)
1103    }
1104
1105    /// Check if two parameters are approximately equal
1106    pub fn parameters_approx_eq(p1: &Parameter, p2: &Parameter, epsilon: f64) -> bool {
1107        match (p1.value(), p2.value()) {
1108            (Some(v1), Some(v2)) => (v1 - v2).abs() < epsilon,
1109            _ => false,
1110        }
1111    }
1112}