use crate::circuit::bit::Qubit;
use crate::circuit::circuit_param::{CircuitParam, ParameterValue};
use crate::circuit::error::CircuitError;
use crate::circuit::gate::{StandardGate, instruction::Instruction};
use crate::circuit::value_instruction::ValueInstruction;
use alloc::borrow::Cow;
use ndarray::Array2;
use num_complex::Complex64;
use smallvec::{SmallVec, smallvec};
use std::fmt;
#[derive(Debug, Clone)]
pub struct Operation {
pub instruction: Instruction,
pub qubits: SmallVec<[Qubit; 3]>,
pub params: SmallVec<[CircuitParam; 1]>,
pub label: Option<Box<str>>,
}
impl PartialEq for Operation {
fn eq(&self, other: &Self) -> bool {
self.instruction == other.instruction
&& self.qubits == other.qubits
&& self.params == other.params
&& self.label == other.label
}
}
impl Operation {
pub fn standard_gate(&self) -> Option<StandardGate> {
self.instruction.standard_gate()
}
pub fn matrix(&self) -> Result<Cow<'_, Array2<Complex64>>, CircuitError> {
let mut ps: SmallVec<[f64; 4]> = smallvec![];
for p in self.params.iter() {
match p {
CircuitParam::Fixed(val) => {
ps.push(*val);
}
CircuitParam::Index(_index) => {
return Err(CircuitError::SymbolicParameterError);
}
}
}
self.instruction
.matrix(&ps)
.ok_or(CircuitError::NoMatrixRepresentation)
}
}
#[derive(Debug, Clone)]
pub struct ValueOperation {
pub instruction: ValueInstruction,
pub qubits: SmallVec<[Qubit; 3]>,
pub params: SmallVec<[ParameterValue; 1]>,
pub label: Option<Box<str>>,
}
impl PartialEq for ValueOperation {
fn eq(&self, other: &Self) -> bool {
self.instruction == other.instruction
&& self.qubits == other.qubits
&& self.params == other.params
&& self.label == other.label
}
}
impl ValueOperation {
pub fn from_standard(
gate: StandardGate,
qubits: impl IntoIterator<Item = Qubit>,
params: impl IntoIterator<Item = ParameterValue>,
) -> Self {
Self {
instruction: ValueInstruction::from_instruction(Instruction::Standard(gate)),
qubits: qubits.into_iter().collect(),
params: params.into_iter().collect(),
label: None,
}
}
pub fn name(&self) -> String {
self.instruction.name()
}
pub fn num_qubits(&self) -> usize {
self.qubits.len()
}
pub fn num_params(&self) -> usize {
self.params.len()
}
pub fn instruction_type(&self) -> &'static str {
self.instruction.instruction_type()
}
pub fn is_standard(&self) -> bool {
self.instruction.is_standard()
}
pub fn is_mcgate(&self) -> bool {
self.instruction.is_mcgate()
}
pub fn is_unitary(&self) -> bool {
self.instruction.is_unitary()
}
pub fn is_circuit_gate(&self) -> bool {
self.instruction.is_circuit_gate()
}
pub fn is_directive(&self) -> bool {
self.instruction.is_directive()
}
pub fn is_classical_data(&self) -> bool {
self.instruction.is_classical_data()
}
pub fn is_classical_control(&self) -> bool {
self.instruction.is_classical_control()
}
pub fn is_delay(&self) -> bool {
self.instruction.is_delay()
}
}
impl fmt::Display for ValueOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.instruction)?;
if !self.params.is_empty() {
write!(f, "(")?;
for (i, param) in self.params.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", crate::circuit::Parameter::from(param))?;
}
write!(f, ")")?;
}
for qubit in &self.qubits {
write!(f, " {}", qubit)?;
}
if let Some(ref label) = self.label {
write!(f, " [{}]", label)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn operation_standard_gate_query_delegates_to_instruction() {
let standard = Operation {
instruction: Instruction::Standard(StandardGate::H),
qubits: smallvec![Qubit::new(0)],
params: smallvec![],
label: None,
};
let directive = Operation {
instruction: Instruction::Directive(crate::circuit::Directive::Barrier),
qubits: smallvec![],
params: smallvec![],
label: None,
};
assert_eq!(standard.standard_gate(), Some(StandardGate::H));
assert_eq!(directive.standard_gate(), None);
}
#[test]
fn value_operation_exposes_instance_queries() {
let operation = ValueOperation::from_standard(
StandardGate::RX,
[Qubit::new(0)],
[ParameterValue::Fixed(0.5)],
);
assert_eq!(operation.name(), "RX");
assert_eq!(operation.num_qubits(), 1);
assert_eq!(operation.num_params(), 1);
assert_eq!(operation.instruction_type(), "standard");
assert!(operation.is_standard());
assert!(!operation.is_directive());
assert!(!operation.is_classical_control());
}
#[test]
fn value_operation_compares_structurally() {
let make = || {
ValueOperation::from_standard(
StandardGate::RX,
[Qubit::new(0)],
[ParameterValue::Fixed(0.5)],
)
};
assert_eq!(make(), make());
let different_param = ValueOperation::from_standard(
StandardGate::RX,
[Qubit::new(0)],
[ParameterValue::Fixed(1.5)],
);
assert_ne!(make(), different_param);
let different_qubit = ValueOperation::from_standard(
StandardGate::RX,
[Qubit::new(1)],
[ParameterValue::Fixed(0.5)],
);
assert_ne!(make(), different_qubit);
let labeled = ValueOperation {
label: Some("tagged".into()),
..make()
};
assert_ne!(make(), labeled);
}
#[test]
fn value_operation_parameter_forms_do_not_cross_compare() {
let symbolic = ValueOperation::from_standard(
StandardGate::RX,
[Qubit::new(0)],
[ParameterValue::Param(crate::circuit::Parameter::symbol(
"theta",
))],
);
let symbolic_again = ValueOperation::from_standard(
StandardGate::RX,
[Qubit::new(0)],
[ParameterValue::Param(crate::circuit::Parameter::symbol(
"theta",
))],
);
let fixed = ValueOperation::from_standard(
StandardGate::RX,
[Qubit::new(0)],
[ParameterValue::Fixed(0.5)],
);
assert_eq!(symbolic, symbolic_again);
assert_ne!(symbolic, fixed);
}
}