use quantrs2_core::gate::{multi, single, GateOp};
use quantrs2_core::qubit::QubitId;
use std::any::Any;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy)]
pub struct GateCost {
pub duration_ns: f64,
pub gate_count: u32,
pub computational_cost: f64,
}
impl GateCost {
#[must_use]
pub const fn new(duration_ns: f64, gate_count: u32, computational_cost: f64) -> Self {
Self {
duration_ns,
gate_count,
computational_cost,
}
}
#[must_use]
pub fn total_cost(&self, time_weight: f64, count_weight: f64, comp_weight: f64) -> f64 {
comp_weight.mul_add(
self.computational_cost,
time_weight.mul_add(self.duration_ns, count_weight * f64::from(self.gate_count)),
)
}
}
#[derive(Debug, Clone, Copy)]
pub struct GateError {
pub fidelity: f64,
pub error_rate: f64,
pub coherent_error: f64,
}
impl GateError {
#[must_use]
pub const fn new(fidelity: f64, error_rate: f64, coherent_error: f64) -> Self {
Self {
fidelity,
error_rate,
coherent_error,
}
}
#[must_use]
pub fn total_error(&self) -> f64 {
1.0 - self.fidelity + self.error_rate + self.coherent_error
}
}
#[derive(Debug, Clone)]
pub struct DecompositionRule {
pub name: String,
pub target_gates: Vec<String>,
pub cost: GateCost,
pub priority: u32,
}
#[derive(Debug, Clone)]
pub struct GateProperties {
pub name: String,
pub num_qubits: usize,
pub is_native: bool,
pub cost: GateCost,
pub error: GateError,
pub decompositions: Vec<DecompositionRule>,
pub commutes_with: Vec<String>,
pub is_self_inverse: bool,
pub is_diagonal: bool,
pub is_parameterized: bool,
}
impl GateProperties {
#[must_use]
pub fn single_qubit(name: &str) -> Self {
match name {
"H" => Self {
name: name.to_string(),
num_qubits: 1,
is_native: true,
cost: GateCost::new(20.0, 1, 1.0),
error: GateError::new(0.9999, 0.0001, 0.00001),
decompositions: vec![],
commutes_with: vec![],
is_self_inverse: true,
is_diagonal: false,
is_parameterized: false,
},
"X" | "Y" | "Z" => Self {
name: name.to_string(),
num_qubits: 1,
is_native: true,
cost: GateCost::new(20.0, 1, 1.0),
error: GateError::new(0.9999, 0.0001, 0.00001),
decompositions: vec![],
commutes_with: if name == "Z" {
vec![
"Z".to_string(),
"RZ".to_string(),
"S".to_string(),
"T".to_string(),
]
} else {
vec![]
},
is_self_inverse: true,
is_diagonal: name == "Z",
is_parameterized: false,
},
"S" => Self {
name: name.to_string(),
num_qubits: 1,
is_native: true,
cost: GateCost::new(20.0, 1, 1.0),
error: GateError::new(0.9999, 0.0001, 0.00001),
decompositions: vec![DecompositionRule {
name: "Z-rotation".to_string(),
target_gates: vec!["RZ".to_string()],
cost: GateCost::new(20.0, 1, 1.0),
priority: 1,
}],
commutes_with: vec![
"Z".to_string(),
"RZ".to_string(),
"S".to_string(),
"T".to_string(),
],
is_self_inverse: false,
is_diagonal: true,
is_parameterized: false,
},
"T" => Self {
name: name.to_string(),
num_qubits: 1,
is_native: true,
cost: GateCost::new(20.0, 1, 1.0),
error: GateError::new(0.9999, 0.0001, 0.00001),
decompositions: vec![DecompositionRule {
name: "Z-rotation".to_string(),
target_gates: vec!["RZ".to_string()],
cost: GateCost::new(20.0, 1, 1.0),
priority: 1,
}],
commutes_with: vec![
"Z".to_string(),
"RZ".to_string(),
"S".to_string(),
"T".to_string(),
],
is_self_inverse: false,
is_diagonal: true,
is_parameterized: false,
},
"RX" | "RY" | "RZ" => Self {
name: name.to_string(),
num_qubits: 1,
is_native: true,
cost: GateCost::new(40.0, 1, 1.5),
error: GateError::new(0.9998, 0.0002, 0.00002),
decompositions: vec![],
commutes_with: if name == "RZ" {
vec![
"Z".to_string(),
"RZ".to_string(),
"S".to_string(),
"T".to_string(),
]
} else {
vec![]
},
is_self_inverse: false,
is_diagonal: name == "RZ",
is_parameterized: true,
},
_ => Self::default_single_qubit(name),
}
}
#[must_use]
pub fn two_qubit(name: &str) -> Self {
match name {
"CNOT" => Self {
name: name.to_string(),
num_qubits: 2,
is_native: true,
cost: GateCost::new(300.0, 1, 3.0),
error: GateError::new(0.999, 0.001, 0.0001),
decompositions: vec![],
commutes_with: vec![],
is_self_inverse: true,
is_diagonal: false,
is_parameterized: false,
},
"CZ" => Self {
name: name.to_string(),
num_qubits: 2,
is_native: true,
cost: GateCost::new(200.0, 1, 2.5),
error: GateError::new(0.999, 0.001, 0.0001),
decompositions: vec![DecompositionRule {
name: "H-CNOT-H".to_string(),
target_gates: vec!["H".to_string(), "CNOT".to_string()],
cost: GateCost::new(340.0, 3, 5.0),
priority: 1,
}],
commutes_with: vec!["CZ".to_string()],
is_self_inverse: true,
is_diagonal: true,
is_parameterized: false,
},
"SWAP" => Self {
name: name.to_string(),
num_qubits: 2,
is_native: false,
cost: GateCost::new(900.0, 3, 9.0),
error: GateError::new(0.997, 0.003, 0.0003),
decompositions: vec![DecompositionRule {
name: "3-CNOT".to_string(),
target_gates: vec!["CNOT".to_string()],
cost: GateCost::new(900.0, 3, 9.0),
priority: 1,
}],
commutes_with: vec![],
is_self_inverse: true,
is_diagonal: false,
is_parameterized: false,
},
_ => Self::default_two_qubit(name),
}
}
#[must_use]
pub fn multi_qubit(name: &str, num_qubits: usize) -> Self {
match name {
"Toffoli" => Self {
name: name.to_string(),
num_qubits: 3,
is_native: false,
cost: GateCost::new(2000.0, 15, 20.0),
error: GateError::new(0.99, 0.01, 0.001),
decompositions: vec![DecompositionRule {
name: "Standard".to_string(),
target_gates: vec![
"H".to_string(),
"CNOT".to_string(),
"T".to_string(),
"T†".to_string(),
],
cost: GateCost::new(2000.0, 15, 20.0),
priority: 1,
}],
commutes_with: vec![],
is_self_inverse: true,
is_diagonal: false,
is_parameterized: false,
},
_ => Self::default_multi_qubit(name, num_qubits),
}
}
fn default_single_qubit(name: &str) -> Self {
Self {
name: name.to_string(),
num_qubits: 1,
is_native: false,
cost: GateCost::new(50.0, 1, 2.0),
error: GateError::new(0.999, 0.001, 0.0001),
decompositions: vec![],
commutes_with: vec![],
is_self_inverse: false,
is_diagonal: false,
is_parameterized: false,
}
}
fn default_two_qubit(name: &str) -> Self {
Self {
name: name.to_string(),
num_qubits: 2,
is_native: false,
cost: GateCost::new(500.0, 2, 5.0),
error: GateError::new(0.995, 0.005, 0.0005),
decompositions: vec![],
commutes_with: vec![],
is_self_inverse: false,
is_diagonal: false,
is_parameterized: false,
}
}
fn default_multi_qubit(name: &str, num_qubits: usize) -> Self {
Self {
name: name.to_string(),
num_qubits,
is_native: false,
cost: GateCost::new(
1000.0 * num_qubits as f64,
num_qubits as u32 * 5,
10.0 * num_qubits as f64,
),
error: GateError::new(
0.99 / num_qubits as f64,
0.01 * num_qubits as f64,
0.001 * num_qubits as f64,
),
decompositions: vec![],
commutes_with: vec![],
is_self_inverse: false,
is_diagonal: false,
is_parameterized: false,
}
}
}
pub struct CommutationTable {
table: HashMap<(String, String), bool>,
}
impl CommutationTable {
#[must_use]
pub fn new() -> Self {
let mut table = HashMap::new();
table.insert(("X".to_string(), "X".to_string()), true);
table.insert(("Y".to_string(), "Y".to_string()), true);
table.insert(("Z".to_string(), "Z".to_string()), true);
table.insert(("X".to_string(), "Y".to_string()), false);
table.insert(("Y".to_string(), "X".to_string()), false);
table.insert(("X".to_string(), "Z".to_string()), false);
table.insert(("Z".to_string(), "X".to_string()), false);
table.insert(("Y".to_string(), "Z".to_string()), false);
table.insert(("Z".to_string(), "Y".to_string()), false);
for diag1 in &["Z", "S", "T", "RZ"] {
for diag2 in &["Z", "S", "T", "RZ"] {
table.insert(((*diag1).to_string(), (*diag2).to_string()), true);
}
}
table.insert(("CNOT".to_string(), "CNOT".to_string()), false);
Self { table }
}
#[must_use]
pub fn commutes(&self, gate1: &str, gate2: &str) -> bool {
if let Some(&result) = self.table.get(&(gate1.to_string(), gate2.to_string())) {
result
} else if let Some(&result) = self.table.get(&(gate2.to_string(), gate1.to_string())) {
result
} else {
false }
}
pub fn gates_commute(&self, gate1: &dyn GateOp, gate2: &dyn GateOp) -> bool {
let qubits1 = gate1.qubits();
let qubits2 = gate2.qubits();
if qubits1.iter().all(|q| !qubits2.contains(q)) {
return true;
}
self.commutes(gate1.name(), gate2.name())
}
}
impl Default for CommutationTable {
fn default() -> Self {
Self::new()
}
}
pub fn get_gate_properties(gate: &dyn GateOp) -> GateProperties {
let num_qubits = gate.num_qubits();
match num_qubits {
1 => GateProperties::single_qubit(gate.name()),
2 => GateProperties::two_qubit(gate.name()),
n => GateProperties::multi_qubit(gate.name(), n),
}
}