use crate::circuit::gate::circuit_gate::{CircuitGate, FrozenCircuit};
use crate::circuit::gate::directive::Directive;
use crate::circuit::gate::standard_gate::StandardGate;
use crate::circuit::gate::{ClassicalDataOp, MCGate, UnitaryGate, gate_matrix};
use crate::circuit::{ClassicalControlOp, ClassicalValue, Parameter};
use alloc::borrow::Cow;
use ndarray::Array2;
use num_complex::Complex64;
use smallvec::SmallVec;
use std::fmt;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)]
pub enum Instruction {
Standard(StandardGate),
McGate(Box<MCGate>),
UnitaryGate(Box<UnitaryGate>),
CircuitGate(Box<CircuitGate>),
Directive(Directive),
ClassicalData(ClassicalDataOp),
ClassicalControl(ClassicalControlOp),
Delay,
}
impl Instruction {
pub fn name(&self) -> String {
match self {
Self::Standard(gate) => gate.name().to_string(),
Self::McGate(gate) => gate.name(),
Self::UnitaryGate(gate) => gate.label().to_string(),
Self::CircuitGate(gate) => gate.name().to_string(),
Self::Directive(directive) => directive.name().to_string(),
Self::ClassicalData(operation) => operation.name().to_string(),
Self::ClassicalControl(operation) => operation.name().to_string(),
Self::Delay => "delay".to_string(),
}
}
pub const fn instruction_type(&self) -> &'static str {
match self {
Self::Standard(_) => "standard",
Self::McGate(_) => "mcgate",
Self::UnitaryGate(_) => "unitary",
Self::CircuitGate(_) => "circuit",
Self::Directive(_) => "directive",
Self::ClassicalData(_) => "classical_data",
Self::ClassicalControl(_) => "classical_control",
Self::Delay => "delay",
}
}
pub const fn is_standard(&self) -> bool {
matches!(self, Self::Standard(_))
}
pub const fn is_mcgate(&self) -> bool {
matches!(self, Self::McGate(_))
}
pub const fn is_unitary(&self) -> bool {
matches!(self, Self::UnitaryGate(_))
}
pub const fn is_circuit_gate(&self) -> bool {
matches!(self, Self::CircuitGate(_))
}
pub const fn is_directive(&self) -> bool {
matches!(self, Self::Directive(_))
}
pub const fn is_classical_data(&self) -> bool {
matches!(self, Self::ClassicalData(_))
}
pub const fn is_classical_control(&self) -> bool {
matches!(self, Self::ClassicalControl(_))
}
pub const fn is_delay(&self) -> bool {
matches!(self, Self::Delay)
}
pub const fn is_quantum_gate(&self) -> bool {
match self {
Self::Standard(_) | Self::McGate(_) | Self::UnitaryGate(_) | Self::CircuitGate(_) => {
true
}
Self::Directive(_)
| Self::ClassicalData(_)
| Self::ClassicalControl(_)
| Self::Delay => false,
}
}
pub fn standard_gate(&self) -> Option<StandardGate> {
match self {
Self::Standard(gate) => Some(*gate),
_ => None,
}
}
pub fn mc_gate(&self) -> Option<&MCGate> {
match self {
Self::McGate(gate) => Some(gate),
_ => None,
}
}
pub fn unitary_gate(&self) -> Option<&UnitaryGate> {
match self {
Self::UnitaryGate(gate) => Some(gate),
_ => None,
}
}
pub fn circuit_gate(&self) -> Option<&CircuitGate> {
match self {
Self::CircuitGate(gate) => Some(gate),
_ => None,
}
}
pub fn directive(&self) -> Option<Directive> {
match self {
Self::Directive(directive) => Some(*directive),
_ => None,
}
}
pub fn classical_data(&self) -> Option<&ClassicalDataOp> {
match self {
Self::ClassicalData(operation) => Some(operation),
_ => None,
}
}
pub fn classical_control(&self) -> Option<&ClassicalControlOp> {
match self {
Self::ClassicalControl(operation) => Some(operation),
_ => None,
}
}
pub fn has_measurement(&self) -> bool {
match self {
Self::Directive(Directive::Measure)
| Self::ClassicalData(ClassicalDataOp::MeasureBit { .. })
| Self::ClassicalData(ClassicalDataOp::MeasureBits { .. }) => true,
Self::ClassicalControl(control) => control.has_measurement(),
_ => false,
}
}
pub fn reads_value(&self, value: ClassicalValue) -> bool {
match self {
Self::ClassicalData(ClassicalDataOp::Store {
value: expression, ..
}) => expression.values().contains(&value),
Self::ClassicalControl(control) => control.reads_value(value),
_ => false,
}
}
fn decompose_standard_control(g: StandardGate) -> (StandardGate, usize) {
match g {
StandardGate::CX => (StandardGate::X, 1),
StandardGate::CCX => (StandardGate::X, 2),
StandardGate::CY => (StandardGate::Y, 1),
StandardGate::CZ => (StandardGate::Z, 1),
StandardGate::CRX => (StandardGate::RX, 1),
StandardGate::CRY => (StandardGate::RY, 1),
StandardGate::CRZ => (StandardGate::RZ, 1),
_ => (g, 0),
}
}
fn compose_standard_control(base: StandardGate, total_controls: usize) -> Option<StandardGate> {
match (base, total_controls) {
(StandardGate::X, 1) => Some(StandardGate::CX),
(StandardGate::X, 2) => Some(StandardGate::CCX),
(StandardGate::Y, 1) => Some(StandardGate::CY),
(StandardGate::Z, 1) => Some(StandardGate::CZ),
(StandardGate::RX, 1) => Some(StandardGate::CRX),
(StandardGate::RY, 1) => Some(StandardGate::CRY),
(StandardGate::RZ, 1) => Some(StandardGate::CRZ),
(gate, 0) => Some(gate),
_ => None,
}
}
pub(crate) fn canonicalize_form(&self) -> Instruction {
match self {
Instruction::McGate(mc) => {
let total_ctrls = mc.num_ctrl_qubits();
let (base, _) = Self::decompose_standard_control(mc.base_gate().to_owned());
if let Some(std) = Self::compose_standard_control(base, total_ctrls) {
Instruction::Standard(std)
} else {
self.clone()
}
}
_ => self.clone(),
}
}
pub fn gate_arity(&self) -> Option<(usize, usize)> {
match self {
Instruction::Standard(gate) => Some((gate.num_qubits(), gate.num_params())),
Instruction::McGate(gate) => Some((gate.num_qubits(), gate.num_params())),
Instruction::UnitaryGate(gate) => {
Some((gate.num_qubits() as usize, gate.num_params() as usize))
}
Instruction::CircuitGate(gate) => Some((gate.num_qubits(), gate.num_params())),
Instruction::Directive(Directive::Measure | Directive::Reset) => Some((1, 0)),
Instruction::ClassicalData(ClassicalDataOp::Store { .. }) => Some((0, 0)),
Instruction::ClassicalData(ClassicalDataOp::MeasureBit { .. }) => Some((1, 0)),
Instruction::Delay => Some((1, 1)),
Instruction::Directive(Directive::Barrier)
| Instruction::ClassicalData(ClassicalDataOp::MeasureBits { .. })
| Instruction::ClassicalControl(_) => None,
}
}
pub fn matrix(&self, params: &[f64]) -> Option<Cow<'_, Array2<Complex64>>> {
match self {
Instruction::Standard(g) => g.matrix(params).ok(),
Instruction::McGate(g) => g.matrix(params).ok(),
Instruction::UnitaryGate(g) => g.matrix_for_params_in_gate_order(params).ok(),
Instruction::CircuitGate(g) => g.matrix_for_params(params).ok().map(Cow::Owned),
Instruction::Directive(_) => None,
Instruction::ClassicalData(_) => None,
Instruction::Delay => None,
Instruction::ClassicalControl(_) => None,
}
}
pub fn inverse(&self, params: &[Parameter]) -> Option<(Instruction, SmallVec<[Parameter; 3]>)> {
match self {
Instruction::Standard(g) => {
if let Some((gate, ps)) = g.inverse(params) {
Some((Self::Standard(gate), ps))
} else {
None
}
}
Instruction::McGate(g) => {
if let Some((gate, ps)) = g.inverse(params) {
Some((Self::McGate(Box::new(gate)), ps))
} else {
None
}
}
Instruction::UnitaryGate(g) => {
if let Some(c) = g.circuit().as_ref() {
if let Ok(c_inv) = c.circuit.inverse() {
let frozen_inv = FrozenCircuit::new(c_inv);
let u_inv = UnitaryGate::new(
format!("{}_dg", g.label()).as_str(),
g.num_qubits(),
g.num_params(),
)
.with_circuit(Arc::new(frozen_inv))
.ok()?;
return Some((Self::UnitaryGate(Box::new(u_inv)), SmallVec::new()));
}
}
None
}
Instruction::CircuitGate(circuit_gate) => {
if let Ok(inv_gate) = circuit_gate.inverse() {
Some((
Instruction::CircuitGate(Box::new(inv_gate)),
params.iter().cloned().collect(),
))
} else {
None
}
}
Instruction::Directive(d) => match d {
Directive::Barrier => Some((Self::Directive(Directive::Barrier), SmallVec::new())),
_ => None,
},
Instruction::ClassicalData(_) => None,
Instruction::Delay => Some((Self::Delay, SmallVec::new())),
Instruction::ClassicalControl(_) => None,
}
}
pub fn control(&self, num_new_ctrls: usize) -> Option<Instruction> {
if num_new_ctrls == 0 {
return Some(self.clone());
}
match self {
Instruction::Standard(g) => {
let (base, curr_ctrls) = Self::decompose_standard_control(*g);
let total_ctrls = curr_ctrls + num_new_ctrls;
if let Some(std) = Self::compose_standard_control(base, total_ctrls) {
Some(Instruction::Standard(std))
} else {
Some(Instruction::McGate(Box::from(MCGate::new(
total_ctrls as u8,
base,
))))
}
}
Instruction::McGate(mc) => {
let total_ctrls = mc.num_ctrl_qubits() + num_new_ctrls;
let (base, _) = Self::decompose_standard_control(mc.base_gate().to_owned());
if let Some(std) = Self::compose_standard_control(base, total_ctrls) {
Some(Instruction::Standard(std))
} else {
Some(Instruction::McGate(Box::from(MCGate::new(
total_ctrls as u8,
base,
))))
}
}
Instruction::UnitaryGate(uni) => {
let mut g = UnitaryGate::new(
format!("ctl_{}_{}", num_new_ctrls, uni.label()).as_str(),
uni.num_qubits() + num_new_ctrls as u16,
uni.num_params(),
);
if let Some(m) = uni.matrix() {
let controlled = gate_matrix::control_matrix(m, num_new_ctrls);
g = match g.with_matrix(controlled) {
Ok(gate) => gate,
Err(_) => return None,
};
} else if let Some(m) = uni.symbolic_matrix() {
let controlled =
crate::circuit::symbolic_matrix::gate::control_matrix(m, num_new_ctrls);
let params = uni.matrix_params().unwrap_or(&[]);
g = match g.with_symbolic_matrix(params.iter().cloned(), controlled) {
Ok(gate) => gate,
Err(_) => return None,
};
}
if let Some(c) = uni.circuit() {
g = match g.with_circuit(c.clone()) {
Ok(gate) => gate,
Err(_) => return None,
};
}
Some(Instruction::UnitaryGate(Box::from(g)))
}
Instruction::CircuitGate(_) => None, Instruction::Directive(_) => None,
Instruction::ClassicalData(_) => None,
Instruction::Delay => None,
Instruction::ClassicalControl(_) => None,
}
}
}
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.name())
}
}
impl From<StandardGate> for Instruction {
fn from(g: StandardGate) -> Self {
Self::Standard(g)
}
}
impl From<Directive> for Instruction {
fn from(d: Directive) -> Self {
Self::Directive(d)
}
}
impl From<ClassicalControlOp> for Instruction {
fn from(cf: ClassicalControlOp) -> Self {
Self::ClassicalControl(cf)
}
}
impl From<ClassicalDataOp> for Instruction {
fn from(op: ClassicalDataOp) -> Self {
Self::ClassicalData(op)
}
}
#[cfg(test)]
#[path = "instruction_test.rs"]
mod instruction_test;