use scirs2_core::Complex64;
use std::collections::HashMap;
use std::ops::{Add, Div, Mul, Sub};
use crate::error::QuantRS2Result;
use crate::gate::GateOp;
use crate::qubit::QubitId;
use crate::symbolic::SymbolicExpression;
#[derive(Debug, Clone)]
pub enum Parameter {
Constant(f64),
ComplexConstant(Complex64),
Symbol(SymbolicParameter),
Symbolic(SymbolicExpression),
}
impl Parameter {
pub const fn constant(value: f64) -> Self {
Self::Constant(value)
}
pub const fn complex_constant(value: Complex64) -> Self {
Self::ComplexConstant(value)
}
pub fn symbol(name: &str) -> Self {
Self::Symbol(SymbolicParameter::new(name))
}
pub fn symbol_with_value(name: &str, value: f64) -> Self {
Self::Symbol(SymbolicParameter::with_value(name, value))
}
pub const fn symbolic(expr: SymbolicExpression) -> Self {
Self::Symbolic(expr)
}
pub fn variable(name: &str) -> Self {
Self::Symbolic(SymbolicExpression::variable(name))
}
pub fn parse(expr: &str) -> QuantRS2Result<Self> {
if let Ok(value) = expr.parse::<f64>() {
return Ok(Self::Constant(value));
}
let symbolic_expr = SymbolicExpression::parse(expr)?;
Ok(Self::Symbolic(symbolic_expr))
}
pub fn value(&self) -> Option<f64> {
match self {
Self::Constant(val) => Some(*val),
Self::ComplexConstant(val) => {
if val.im.abs() < 1e-12 {
Some(val.re)
} else {
None }
}
Self::Symbol(sym) => sym.value,
Self::Symbolic(expr) => {
expr.evaluate(&HashMap::new()).ok()
}
}
}
pub fn complex_value(&self) -> Option<Complex64> {
match self {
Self::Constant(val) => Some(Complex64::new(*val, 0.0)),
Self::ComplexConstant(val) => Some(*val),
Self::Symbol(sym) => sym.value.map(|v| Complex64::new(v, 0.0)),
Self::Symbolic(expr) => {
expr.evaluate_complex(&HashMap::new()).ok()
}
}
}
pub fn has_value(&self) -> bool {
match self {
Self::Constant(_) | Self::ComplexConstant(_) => true,
Self::Symbol(sym) => sym.value.is_some(),
Self::Symbolic(expr) => expr.is_constant(),
}
}
pub fn evaluate(&self, variables: &HashMap<String, f64>) -> QuantRS2Result<f64> {
match self {
Self::Constant(val) => Ok(*val),
Self::ComplexConstant(val) => {
if val.im.abs() < 1e-12 {
Ok(val.re)
} else {
Err(crate::error::QuantRS2Error::InvalidInput(
"Cannot evaluate complex parameter to real number".to_string(),
))
}
}
Self::Symbol(sym) => sym.value.map_or_else(
|| {
variables.get(&sym.name).copied().ok_or_else(|| {
crate::error::QuantRS2Error::InvalidInput(format!(
"Variable '{}' not found",
sym.name
))
})
},
Ok,
),
Self::Symbolic(expr) => expr.evaluate(variables),
}
}
pub fn evaluate_complex(
&self,
variables: &HashMap<String, Complex64>,
) -> QuantRS2Result<Complex64> {
match self {
Self::Constant(val) => Ok(Complex64::new(*val, 0.0)),
Self::ComplexConstant(val) => Ok(*val),
Self::Symbol(sym) => sym.value.map_or_else(
|| {
variables.get(&sym.name).copied().ok_or_else(|| {
crate::error::QuantRS2Error::InvalidInput(format!(
"Variable '{}' not found",
sym.name
))
})
},
|value| Ok(Complex64::new(value, 0.0)),
),
Self::Symbolic(expr) => expr.evaluate_complex(variables),
}
}
pub fn variables(&self) -> Vec<String> {
match self {
Self::Constant(_) | Self::ComplexConstant(_) => Vec::new(),
Self::Symbol(sym) => {
if sym.value.is_some() {
Vec::new()
} else {
vec![sym.name.clone()]
}
}
Self::Symbolic(expr) => expr.variables(),
}
}
pub fn substitute(&self, substitutions: &HashMap<String, Self>) -> QuantRS2Result<Self> {
match self {
Self::Constant(_) | Self::ComplexConstant(_) => Ok(self.clone()),
Self::Symbol(sym) => Ok(substitutions
.get(&sym.name)
.map_or_else(|| self.clone(), |r| r.clone())),
Self::Symbolic(expr) => {
let symbolic_subs: HashMap<String, SymbolicExpression> = substitutions
.iter()
.map(|(k, v)| (k.clone(), v.to_symbolic_expression()))
.collect();
let new_expr = expr.substitute(&symbolic_subs)?;
Ok(Self::Symbolic(new_expr))
}
}
}
pub fn to_symbolic_expression(&self) -> SymbolicExpression {
match self {
Self::Constant(val) => SymbolicExpression::constant(*val),
Self::ComplexConstant(val) => SymbolicExpression::complex_constant(*val),
Self::Symbol(sym) => sym.value.map_or_else(
|| SymbolicExpression::variable(&sym.name),
SymbolicExpression::constant,
),
Self::Symbolic(expr) => expr.clone(),
}
}
#[cfg(feature = "symbolic")]
pub fn diff(&self, var: &str) -> QuantRS2Result<Self> {
use crate::symbolic::calculus;
let expr = self.to_symbolic_expression();
let diff_expr = calculus::diff(&expr, var)?;
Ok(Self::Symbolic(diff_expr))
}
#[cfg(feature = "symbolic")]
pub fn integrate(&self, var: &str) -> QuantRS2Result<Self> {
use crate::symbolic::calculus;
let expr = self.to_symbolic_expression();
let int_expr = calculus::integrate(&expr, var)?;
Ok(Self::Symbolic(int_expr))
}
}
impl From<f64> for Parameter {
fn from(value: f64) -> Self {
Self::Constant(value)
}
}
impl From<Complex64> for Parameter {
fn from(value: Complex64) -> Self {
if value.im == 0.0 {
Self::Constant(value.re)
} else {
Self::ComplexConstant(value)
}
}
}
impl From<SymbolicExpression> for Parameter {
fn from(expr: SymbolicExpression) -> Self {
Self::Symbolic(expr)
}
}
impl From<&str> for Parameter {
fn from(name: &str) -> Self {
Self::variable(name)
}
}
impl Add for Parameter {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::Constant(a), Self::Constant(b)) => Self::Constant(a + b),
(Self::ComplexConstant(a), Self::ComplexConstant(b)) => Self::ComplexConstant(a + b),
(Self::Constant(a), Self::ComplexConstant(b)) => {
Self::ComplexConstant(Complex64::new(a, 0.0) + b)
}
(Self::ComplexConstant(a), Self::Constant(b)) => {
Self::ComplexConstant(a + Complex64::new(b, 0.0))
}
(a, b) => {
let a_expr = a.to_symbolic_expression();
let b_expr = b.to_symbolic_expression();
Self::Symbolic(a_expr + b_expr)
}
}
}
}
impl Sub for Parameter {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::Constant(a), Self::Constant(b)) => Self::Constant(a - b),
(Self::ComplexConstant(a), Self::ComplexConstant(b)) => Self::ComplexConstant(a - b),
(Self::Constant(a), Self::ComplexConstant(b)) => {
Self::ComplexConstant(Complex64::new(a, 0.0) - b)
}
(Self::ComplexConstant(a), Self::Constant(b)) => {
Self::ComplexConstant(a - Complex64::new(b, 0.0))
}
(a, b) => {
let a_expr = a.to_symbolic_expression();
let b_expr = b.to_symbolic_expression();
Self::Symbolic(a_expr - b_expr)
}
}
}
}
impl Mul for Parameter {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::Constant(a), Self::Constant(b)) => Self::Constant(a * b),
(Self::ComplexConstant(a), Self::ComplexConstant(b)) => Self::ComplexConstant(a * b),
(Self::Constant(a), Self::ComplexConstant(b)) => {
Self::ComplexConstant(Complex64::new(a, 0.0) * b)
}
(Self::ComplexConstant(a), Self::Constant(b)) => {
Self::ComplexConstant(a * Complex64::new(b, 0.0))
}
(a, b) => {
let a_expr = a.to_symbolic_expression();
let b_expr = b.to_symbolic_expression();
Self::Symbolic(a_expr * b_expr)
}
}
}
}
impl Div for Parameter {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::Constant(a), Self::Constant(b)) => Self::Constant(a / b),
(Self::ComplexConstant(a), Self::ComplexConstant(b)) => Self::ComplexConstant(a / b),
(Self::Constant(a), Self::ComplexConstant(b)) => {
Self::ComplexConstant(Complex64::new(a, 0.0) / b)
}
(Self::ComplexConstant(a), Self::Constant(b)) => {
Self::ComplexConstant(a / Complex64::new(b, 0.0))
}
(a, b) => {
let a_expr = a.to_symbolic_expression();
let b_expr = b.to_symbolic_expression();
Self::Symbolic(a_expr / b_expr)
}
}
}
}
#[derive(Debug, Clone)]
pub struct SymbolicParameter {
pub name: String,
pub value: Option<f64>,
}
impl SymbolicParameter {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
value: None,
}
}
pub fn with_value(name: &str, value: f64) -> Self {
Self {
name: name.to_string(),
value: Some(value),
}
}
pub const fn set_value(&mut self, value: f64) {
self.value = Some(value);
}
pub const fn clear_value(&mut self) {
self.value = None;
}
}
pub trait ParametricGate: GateOp {
fn parameters(&self) -> Vec<Parameter>;
fn parameter_names(&self) -> Vec<String>;
fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>>;
fn with_parameter_at(
&self,
index: usize,
param: Parameter,
) -> QuantRS2Result<Box<dyn ParametricGate>>;
fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>>;
}
#[derive(Debug, Clone)]
pub struct ParametricRotationX {
pub target: QubitId,
pub theta: Parameter,
}
impl ParametricRotationX {
pub const fn new(target: QubitId, theta: f64) -> Self {
Self {
target,
theta: Parameter::constant(theta),
}
}
pub fn new_symbolic(target: QubitId, name: &str) -> Self {
Self {
target,
theta: Parameter::symbol(name),
}
}
}
impl GateOp for ParametricRotationX {
fn name(&self) -> &'static str {
"RX"
}
fn qubits(&self) -> Vec<QubitId> {
vec![self.target]
}
fn is_parameterized(&self) -> bool {
true
}
fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
self.theta.value().map_or_else(
|| {
Err(crate::error::QuantRS2Error::UnsupportedOperation(
"Cannot generate matrix for RX gate with unbound symbolic parameter".into(),
))
},
|theta| {
let cos = (theta / 2.0).cos();
let sin = (theta / 2.0).sin();
Ok(vec![
Complex64::new(cos, 0.0),
Complex64::new(0.0, -sin),
Complex64::new(0.0, -sin),
Complex64::new(cos, 0.0),
])
},
)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_gate(&self) -> Box<dyn GateOp> {
Box::new(self.clone())
}
}
impl ParametricGate for ParametricRotationX {
fn parameters(&self) -> Vec<Parameter> {
vec![self.theta.clone()]
}
fn parameter_names(&self) -> Vec<String> {
match self.theta {
Parameter::Symbol(ref sym) => vec![sym.name.clone()],
_ => Vec::new(),
}
}
fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
if params.len() != 1 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"RotationX expects 1 parameter, got {}",
params.len()
)));
}
Ok(Box::new(Self {
target: self.target,
theta: params[0].clone(),
}))
}
fn with_parameter_at(
&self,
index: usize,
param: Parameter,
) -> QuantRS2Result<Box<dyn ParametricGate>> {
if index != 0 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"RotationX has only 1 parameter, got index {index}"
)));
}
Ok(Box::new(Self {
target: self.target,
theta: param,
}))
}
fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
match self.theta {
Parameter::Symbol(ref sym) => {
for (name, value) in values {
if sym.name == *name {
return Ok(Box::new(Self {
target: self.target,
theta: Parameter::Symbol(SymbolicParameter::with_value(
&sym.name, *value,
)),
}));
}
}
Ok(Box::new(self.clone()))
}
_ => Ok(Box::new(self.clone())), }
}
fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
self.assign(values)
}
}
#[derive(Debug, Clone)]
pub struct ParametricRotationY {
pub target: QubitId,
pub theta: Parameter,
}
impl ParametricRotationY {
pub const fn new(target: QubitId, theta: f64) -> Self {
Self {
target,
theta: Parameter::constant(theta),
}
}
pub fn new_symbolic(target: QubitId, name: &str) -> Self {
Self {
target,
theta: Parameter::symbol(name),
}
}
}
impl GateOp for ParametricRotationY {
fn name(&self) -> &'static str {
"RY"
}
fn qubits(&self) -> Vec<QubitId> {
vec![self.target]
}
fn is_parameterized(&self) -> bool {
true
}
fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
self.theta.value().map_or_else(
|| {
Err(crate::error::QuantRS2Error::UnsupportedOperation(
"Cannot generate matrix for RY gate with unbound symbolic parameter".into(),
))
},
|theta| {
let cos = (theta / 2.0).cos();
let sin = (theta / 2.0).sin();
Ok(vec![
Complex64::new(cos, 0.0),
Complex64::new(-sin, 0.0),
Complex64::new(sin, 0.0),
Complex64::new(cos, 0.0),
])
},
)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_gate(&self) -> Box<dyn GateOp> {
Box::new(self.clone())
}
}
impl ParametricGate for ParametricRotationY {
fn parameters(&self) -> Vec<Parameter> {
vec![self.theta.clone()]
}
fn parameter_names(&self) -> Vec<String> {
match self.theta {
Parameter::Symbol(ref sym) => vec![sym.name.clone()],
_ => Vec::new(),
}
}
fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
if params.len() != 1 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"RotationY expects 1 parameter, got {}",
params.len()
)));
}
Ok(Box::new(Self {
target: self.target,
theta: params[0].clone(),
}))
}
fn with_parameter_at(
&self,
index: usize,
param: Parameter,
) -> QuantRS2Result<Box<dyn ParametricGate>> {
if index != 0 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"RotationY has only 1 parameter, got index {index}"
)));
}
Ok(Box::new(Self {
target: self.target,
theta: param,
}))
}
fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
match self.theta {
Parameter::Symbol(ref sym) => {
for (name, value) in values {
if sym.name == *name {
return Ok(Box::new(Self {
target: self.target,
theta: Parameter::Symbol(SymbolicParameter::with_value(
&sym.name, *value,
)),
}));
}
}
Ok(Box::new(self.clone()))
}
_ => Ok(Box::new(self.clone())), }
}
fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
self.assign(values)
}
}
#[derive(Debug, Clone)]
pub struct ParametricRotationZ {
pub target: QubitId,
pub theta: Parameter,
}
impl ParametricRotationZ {
pub const fn new(target: QubitId, theta: f64) -> Self {
Self {
target,
theta: Parameter::constant(theta),
}
}
pub fn new_symbolic(target: QubitId, name: &str) -> Self {
Self {
target,
theta: Parameter::symbol(name),
}
}
}
impl GateOp for ParametricRotationZ {
fn name(&self) -> &'static str {
"RZ"
}
fn qubits(&self) -> Vec<QubitId> {
vec![self.target]
}
fn is_parameterized(&self) -> bool {
true
}
fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
self.theta.value().map_or_else(
|| {
Err(crate::error::QuantRS2Error::UnsupportedOperation(
"Cannot generate matrix for RZ gate with unbound symbolic parameter".into(),
))
},
|theta| {
let phase = Complex64::new(0.0, -theta / 2.0).exp();
let phase_conj = Complex64::new(0.0, theta / 2.0).exp();
Ok(vec![
phase_conj,
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
phase,
])
},
)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_gate(&self) -> Box<dyn GateOp> {
Box::new(self.clone())
}
}
impl ParametricGate for ParametricRotationZ {
fn parameters(&self) -> Vec<Parameter> {
vec![self.theta.clone()]
}
fn parameter_names(&self) -> Vec<String> {
match self.theta {
Parameter::Symbol(ref sym) => vec![sym.name.clone()],
_ => Vec::new(),
}
}
fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
if params.len() != 1 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"RotationZ expects 1 parameter, got {}",
params.len()
)));
}
Ok(Box::new(Self {
target: self.target,
theta: params[0].clone(),
}))
}
fn with_parameter_at(
&self,
index: usize,
param: Parameter,
) -> QuantRS2Result<Box<dyn ParametricGate>> {
if index != 0 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"RotationZ has only 1 parameter, got index {index}"
)));
}
Ok(Box::new(Self {
target: self.target,
theta: param,
}))
}
fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
match self.theta {
Parameter::Symbol(ref sym) => {
for (name, value) in values {
if sym.name == *name {
return Ok(Box::new(Self {
target: self.target,
theta: Parameter::Symbol(SymbolicParameter::with_value(
&sym.name, *value,
)),
}));
}
}
Ok(Box::new(self.clone()))
}
_ => Ok(Box::new(self.clone())), }
}
fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
self.assign(values)
}
}
#[derive(Debug, Clone)]
pub struct ParametricU {
pub target: QubitId,
pub theta: Parameter,
pub phi: Parameter,
pub lambda: Parameter,
}
impl ParametricU {
pub const fn new(target: QubitId, theta: f64, phi: f64, lambda: f64) -> Self {
Self {
target,
theta: Parameter::constant(theta),
phi: Parameter::constant(phi),
lambda: Parameter::constant(lambda),
}
}
pub fn new_symbolic(
target: QubitId,
theta_name: &str,
phi_name: &str,
lambda_name: &str,
) -> Self {
Self {
target,
theta: Parameter::symbol(theta_name),
phi: Parameter::symbol(phi_name),
lambda: Parameter::symbol(lambda_name),
}
}
}
impl GateOp for ParametricU {
fn name(&self) -> &'static str {
"U"
}
fn qubits(&self) -> Vec<QubitId> {
vec![self.target]
}
fn is_parameterized(&self) -> bool {
true
}
fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
if let (Some(theta), Some(phi), Some(lambda)) =
(self.theta.value(), self.phi.value(), self.lambda.value())
{
let cos = (theta / 2.0).cos();
let sin = (theta / 2.0).sin();
let e_phi = Complex64::new(0.0, phi).exp();
let e_lambda = Complex64::new(0.0, lambda).exp();
Ok(vec![
Complex64::new(cos, 0.0),
-sin * e_lambda,
sin * e_phi,
cos * e_phi * e_lambda,
])
} else {
Err(crate::error::QuantRS2Error::UnsupportedOperation(
"Cannot generate matrix for U gate with unbound symbolic parameters".into(),
))
}
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_gate(&self) -> Box<dyn GateOp> {
Box::new(self.clone())
}
}
impl ParametricGate for ParametricU {
fn parameters(&self) -> Vec<Parameter> {
vec![self.theta.clone(), self.phi.clone(), self.lambda.clone()]
}
fn parameter_names(&self) -> Vec<String> {
let mut names = Vec::new();
if let Parameter::Symbol(ref sym) = self.theta {
names.push(sym.name.clone());
}
if let Parameter::Symbol(ref sym) = self.phi {
names.push(sym.name.clone());
}
if let Parameter::Symbol(ref sym) = self.lambda {
names.push(sym.name.clone());
}
names
}
fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
if params.len() != 3 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"U gate expects 3 parameters, got {}",
params.len()
)));
}
Ok(Box::new(Self {
target: self.target,
theta: params[0].clone(),
phi: params[1].clone(),
lambda: params[2].clone(),
}))
}
fn with_parameter_at(
&self,
index: usize,
param: Parameter,
) -> QuantRS2Result<Box<dyn ParametricGate>> {
match index {
0 => Ok(Box::new(Self {
target: self.target,
theta: param,
phi: self.phi.clone(),
lambda: self.lambda.clone(),
})),
1 => Ok(Box::new(Self {
target: self.target,
theta: self.theta.clone(),
phi: param,
lambda: self.lambda.clone(),
})),
2 => Ok(Box::new(Self {
target: self.target,
theta: self.theta.clone(),
phi: self.phi.clone(),
lambda: param,
})),
_ => Err(crate::error::QuantRS2Error::InvalidInput(format!(
"U gate has only 3 parameters, got index {index}"
))),
}
}
fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
let mut result = self.clone();
if let Parameter::Symbol(ref sym) = self.theta {
for (name, value) in values {
if sym.name == *name {
result.theta =
Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
break;
}
}
}
if let Parameter::Symbol(ref sym) = self.phi {
for (name, value) in values {
if sym.name == *name {
result.phi =
Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
break;
}
}
}
if let Parameter::Symbol(ref sym) = self.lambda {
for (name, value) in values {
if sym.name == *name {
result.lambda =
Parameter::Symbol(SymbolicParameter::with_value(&sym.name, *value));
break;
}
}
}
Ok(Box::new(result))
}
fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
self.assign(values)
}
}
#[derive(Debug, Clone)]
pub struct ParametricCRX {
pub control: QubitId,
pub target: QubitId,
pub theta: Parameter,
}
impl ParametricCRX {
pub const fn new(control: QubitId, target: QubitId, theta: f64) -> Self {
Self {
control,
target,
theta: Parameter::constant(theta),
}
}
pub fn new_symbolic(control: QubitId, target: QubitId, name: &str) -> Self {
Self {
control,
target,
theta: Parameter::symbol(name),
}
}
}
impl GateOp for ParametricCRX {
fn name(&self) -> &'static str {
"CRX"
}
fn qubits(&self) -> Vec<QubitId> {
vec![self.control, self.target]
}
fn is_parameterized(&self) -> bool {
true
}
fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
self.theta.value().map_or_else(
|| {
Err(crate::error::QuantRS2Error::UnsupportedOperation(
"Cannot generate matrix for CRX gate with unbound symbolic parameter".into(),
))
},
|theta| {
let cos = (theta / 2.0).cos();
let sin = (theta / 2.0).sin();
Ok(vec![
Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(cos, 0.0), Complex64::new(0.0, -sin), Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0), Complex64::new(0.0, -sin), Complex64::new(cos, 0.0), ])
},
)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_gate(&self) -> Box<dyn GateOp> {
Box::new(self.clone())
}
}
impl ParametricGate for ParametricCRX {
fn parameters(&self) -> Vec<Parameter> {
vec![self.theta.clone()]
}
fn parameter_names(&self) -> Vec<String> {
match self.theta {
Parameter::Symbol(ref sym) => vec![sym.name.clone()],
_ => Vec::new(),
}
}
fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
if params.len() != 1 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"CRX expects 1 parameter, got {}",
params.len()
)));
}
Ok(Box::new(Self {
control: self.control,
target: self.target,
theta: params[0].clone(),
}))
}
fn with_parameter_at(
&self,
index: usize,
param: Parameter,
) -> QuantRS2Result<Box<dyn ParametricGate>> {
if index != 0 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"CRX has only 1 parameter, got index {index}"
)));
}
Ok(Box::new(Self {
control: self.control,
target: self.target,
theta: param,
}))
}
fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
match self.theta {
Parameter::Symbol(ref sym) => {
for (name, value) in values {
if sym.name == *name {
return Ok(Box::new(Self {
control: self.control,
target: self.target,
theta: Parameter::Symbol(SymbolicParameter::with_value(
&sym.name, *value,
)),
}));
}
}
Ok(Box::new(self.clone()))
}
_ => Ok(Box::new(self.clone())), }
}
fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
self.assign(values)
}
}
#[derive(Debug, Clone)]
pub struct ParametricPhaseShift {
pub target: QubitId,
pub phi: Parameter,
}
impl ParametricPhaseShift {
pub const fn new(target: QubitId, phi: f64) -> Self {
Self {
target,
phi: Parameter::constant(phi),
}
}
pub fn new_symbolic(target: QubitId, name: &str) -> Self {
Self {
target,
phi: Parameter::symbol(name),
}
}
}
impl GateOp for ParametricPhaseShift {
fn name(&self) -> &'static str {
"P"
}
fn qubits(&self) -> Vec<QubitId> {
vec![self.target]
}
fn is_parameterized(&self) -> bool {
true
}
fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
self.phi.value().map_or_else(
|| {
Err(crate::error::QuantRS2Error::UnsupportedOperation(
"Cannot generate matrix for phase shift gate with unbound symbolic parameter"
.into(),
))
},
|phi| {
let phase = Complex64::new(phi.cos(), phi.sin());
Ok(vec![
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
phase,
])
},
)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_gate(&self) -> Box<dyn GateOp> {
Box::new(self.clone())
}
}
impl ParametricGate for ParametricPhaseShift {
fn parameters(&self) -> Vec<Parameter> {
vec![self.phi.clone()]
}
fn parameter_names(&self) -> Vec<String> {
match self.phi {
Parameter::Symbol(ref sym) => vec![sym.name.clone()],
_ => Vec::new(),
}
}
fn with_parameters(&self, params: &[Parameter]) -> QuantRS2Result<Box<dyn ParametricGate>> {
if params.len() != 1 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"Phase shift gate expects 1 parameter, got {}",
params.len()
)));
}
Ok(Box::new(Self {
target: self.target,
phi: params[0].clone(),
}))
}
fn with_parameter_at(
&self,
index: usize,
param: Parameter,
) -> QuantRS2Result<Box<dyn ParametricGate>> {
if index != 0 {
return Err(crate::error::QuantRS2Error::InvalidInput(format!(
"Phase shift gate has only 1 parameter, got index {index}"
)));
}
Ok(Box::new(Self {
target: self.target,
phi: param,
}))
}
fn assign(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
match self.phi {
Parameter::Symbol(ref sym) => {
for (name, value) in values {
if sym.name == *name {
return Ok(Box::new(Self {
target: self.target,
phi: Parameter::Symbol(SymbolicParameter::with_value(
&sym.name, *value,
)),
}));
}
}
Ok(Box::new(self.clone()))
}
_ => Ok(Box::new(self.clone())), }
}
fn bind(&self, values: &[(String, f64)]) -> QuantRS2Result<Box<dyn ParametricGate>> {
self.assign(values)
}
}
pub mod utils {
use super::*;
use crate::gate::{multi, single};
pub fn parametrize_rotation_gate(gate: &dyn GateOp) -> Option<Box<dyn ParametricGate>> {
if !gate.is_parameterized() {
return None;
}
if let Some(rx) = gate.as_any().downcast_ref::<single::RotationX>() {
Some(Box::new(ParametricRotationX::new(rx.target, rx.theta)))
} else if let Some(ry) = gate.as_any().downcast_ref::<single::RotationY>() {
Some(Box::new(ParametricRotationY::new(ry.target, ry.theta)))
} else if let Some(rz) = gate.as_any().downcast_ref::<single::RotationZ>() {
Some(Box::new(ParametricRotationZ::new(rz.target, rz.theta)))
} else if let Some(crx) = gate.as_any().downcast_ref::<multi::CRX>() {
Some(Box::new(ParametricCRX::new(
crx.control,
crx.target,
crx.theta,
)))
} else {
None
}
}
pub fn symbolize_parameter(param: f64, name: &str) -> Parameter {
Parameter::symbol_with_value(name, param)
}
pub fn parameters_approx_eq(p1: &Parameter, p2: &Parameter, epsilon: f64) -> bool {
match (p1.value(), p2.value()) {
(Some(v1), Some(v2)) => (v1 - v2).abs() < epsilon,
_ => false,
}
}
}