use crate::circuit::{Instruction, Parameter, Qubit, StandardGate};
use crate::compile::PARAMETER_EQ_TOLERANCE;
use crate::compile::commutation::algebra::algebraic_commutation;
use crate::compile::commutation::matrix::matrix_commutation;
use crate::compile::commutation::rules::RuleCommutationOracle;
use crate::compile::knowledge::library::RuleLibrary;
use std::sync::OnceLock;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Commutation {
Exact,
UpToGlobalPhase(Parameter),
}
impl Commutation {
pub fn is_exact(&self) -> bool {
matches!(self, Self::Exact)
}
pub fn phase(&self) -> Parameter {
match self {
Self::Exact => Parameter::from(0.0),
Self::UpToGlobalPhase(phase) => phase.clone(),
}
}
}
pub type CommutationResult = Option<Commutation>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommutationConfig {
pub enable_rule_oracle: bool,
pub enable_matrix_fallback: bool,
pub max_matrix_qubits: usize,
}
impl Default for CommutationConfig {
fn default() -> Self {
Self {
enable_rule_oracle: true,
enable_matrix_fallback: true,
max_matrix_qubits: 4,
}
}
}
#[derive(Debug, Clone)]
pub struct CommutationChecker {
rules: Option<RuleCommutationOracle>,
config: CommutationConfig,
}
impl CommutationChecker {
pub fn builtin() -> Self {
Self::with_config(CommutationConfig::default())
}
pub fn with_config(config: CommutationConfig) -> Self {
let rules = if config.enable_rule_oracle {
Some(RuleCommutationOracle::builtin())
} else {
None
};
Self { rules, config }
}
pub fn from_library(library: &RuleLibrary, config: CommutationConfig) -> Self {
let rules = if config.enable_rule_oracle {
Some(RuleCommutationOracle::from_library(library))
} else {
None
};
Self { rules, config }
}
pub const fn config(&self) -> &CommutationConfig {
&self.config
}
pub fn check(
&self,
lhs_inst: &Instruction,
lhs_qubits: &[Qubit],
lhs_params: &[Parameter],
rhs_inst: &Instruction,
rhs_qubits: &[Qubit],
rhs_params: &[Parameter],
) -> CommutationResult {
let (lhs_expected_qubits, lhs_expected_params) = lhs_inst.gate_arity()?;
let (rhs_expected_qubits, rhs_expected_params) = rhs_inst.gate_arity()?;
if lhs_qubits.len() != lhs_expected_qubits
|| lhs_params.len() != lhs_expected_params
|| rhs_qubits.len() != rhs_expected_qubits
|| rhs_params.len() != rhs_expected_params
|| lhs_qubits
.iter()
.enumerate()
.any(|(index, qubit)| lhs_qubits[index + 1..].contains(qubit))
|| rhs_qubits
.iter()
.enumerate()
.any(|(index, qubit)| rhs_qubits[index + 1..].contains(qubit))
{
return None;
}
if matches!(lhs_inst, Instruction::Standard(StandardGate::I))
|| matches!(rhs_inst, Instruction::Standard(StandardGate::I))
|| matches!(lhs_inst, Instruction::McGate(gate) if *gate.base_gate() == StandardGate::I)
|| matches!(rhs_inst, Instruction::McGate(gate) if *gate.base_gate() == StandardGate::I)
|| matches!(lhs_inst, Instruction::Standard(StandardGate::GPhase))
|| matches!(rhs_inst, Instruction::Standard(StandardGate::GPhase))
|| !lhs_qubits.iter().any(|qubit| rhs_qubits.contains(qubit))
{
return Some(Commutation::Exact);
}
if same_application(
lhs_inst, lhs_qubits, lhs_params, rhs_inst, rhs_qubits, rhs_params,
) {
return Some(Commutation::Exact);
}
if let Some(result) = algebraic_commutation(
lhs_inst, lhs_qubits, lhs_params, rhs_inst, rhs_qubits, rhs_params,
) {
return Some(result);
}
if let Some(rules) = self.rules.as_ref()
&& let Some(result) = rules.check(
lhs_inst, lhs_qubits, lhs_params, rhs_inst, rhs_qubits, rhs_params,
)
{
return Some(result);
}
if self.config.enable_matrix_fallback {
return matrix_commutation(
lhs_inst,
lhs_qubits,
lhs_params,
rhs_inst,
rhs_qubits,
rhs_params,
self.config.max_matrix_qubits,
);
}
None
}
}
pub fn check_commutation(
lhs_inst: &Instruction,
lhs_qubits: &[Qubit],
lhs_params: &[Parameter],
rhs_inst: &Instruction,
rhs_qubits: &[Qubit],
rhs_params: &[Parameter],
) -> CommutationResult {
static CHECKER: OnceLock<CommutationChecker> = OnceLock::new();
CHECKER.get_or_init(CommutationChecker::builtin).check(
lhs_inst, lhs_qubits, lhs_params, rhs_inst, rhs_qubits, rhs_params,
)
}
fn same_application(
lhs_inst: &Instruction,
lhs_qubits: &[Qubit],
lhs_params: &[Parameter],
rhs_inst: &Instruction,
rhs_qubits: &[Qubit],
rhs_params: &[Parameter],
) -> bool {
let same_instruction =
lhs_inst.is_quantum_gate() && rhs_inst.is_quantum_gate() && lhs_inst == rhs_inst;
lhs_qubits == rhs_qubits
&& same_instruction
&& lhs_params.len() == rhs_params.len()
&& lhs_params
.iter()
.zip(rhs_params)
.all(|(lhs, rhs)| lhs.provably_equal(rhs, PARAMETER_EQ_TOLERANCE))
}