use scirs2_core::ndarray::{Array1, Array2};
use std::collections::HashMap;
use super::config::HardwareArchitecture;
use crate::circuit_interfaces::{InterfaceCircuit, InterfaceGateType};
#[derive(Debug, Clone)]
pub struct ParameterizedQuantumCircuit {
pub circuit: InterfaceCircuit,
pub parameters: Array1<f64>,
pub parameter_names: Vec<String>,
pub gate_parameter_map: HashMap<usize, Vec<usize>>,
pub hardware_optimizations: HardwareOptimizations,
}
#[derive(Debug, Clone)]
pub struct HardwareOptimizations {
pub connectivity_graph: Array2<bool>,
pub gate_fidelities: HashMap<String, f64>,
pub decoherence_times: Array1<f64>,
pub gate_times: HashMap<String, f64>,
pub crosstalk_matrix: Array2<f64>,
}
impl ParameterizedQuantumCircuit {
#[must_use]
pub fn new(
circuit: InterfaceCircuit,
parameters: Array1<f64>,
parameter_names: Vec<String>,
hardware_architecture: HardwareArchitecture,
) -> Self {
let num_qubits = circuit.num_qubits;
let hardware_optimizations =
HardwareOptimizations::for_hardware(hardware_architecture, num_qubits);
Self {
circuit,
parameters,
parameter_names,
gate_parameter_map: HashMap::new(),
hardware_optimizations,
}
}
pub fn update_parameters(&mut self, new_parameters: Array1<f64>) -> Result<(), String> {
if new_parameters.len() != self.parameters.len() {
return Err(format!(
"Parameter count mismatch: expected {}, got {}",
self.parameters.len(),
new_parameters.len()
));
}
self.parameters = new_parameters;
Ok(())
}
#[must_use]
pub fn get_parameter(&self, index: usize) -> Option<f64> {
self.parameters.get(index).copied()
}
pub fn set_parameter(&mut self, index: usize, value: f64) -> Result<(), String> {
if index >= self.parameters.len() {
return Err(format!("Parameter index {index} out of bounds"));
}
self.parameters[index] = value;
Ok(())
}
#[must_use]
pub fn num_parameters(&self) -> usize {
self.parameters.len()
}
#[must_use]
pub const fn num_qubits(&self) -> usize {
self.circuit.num_qubits
}
#[must_use]
pub fn depth(&self) -> usize {
self.circuit.gates.len()
}
pub fn add_parameter_mapping(&mut self, gate_index: usize, parameter_indices: Vec<usize>) {
self.gate_parameter_map
.insert(gate_index, parameter_indices);
}
#[must_use]
pub fn get_parameter_mapping(&self, gate_index: usize) -> Option<&Vec<usize>> {
self.gate_parameter_map.get(&gate_index)
}
#[must_use]
pub fn estimate_fidelity(&self) -> f64 {
let mut total_fidelity = 1.0;
for gate in &self.circuit.gates {
let gate_name = Self::gate_type_to_string(&gate.gate_type);
if let Some(&fidelity) = self.hardware_optimizations.gate_fidelities.get(&gate_name) {
total_fidelity *= fidelity;
} else {
total_fidelity *= 0.99;
}
}
total_fidelity
}
#[must_use]
pub fn estimate_execution_time(&self) -> f64 {
let mut total_time = 0.0;
for gate in &self.circuit.gates {
let gate_name = Self::gate_type_to_string(&gate.gate_type);
if let Some(&time) = self.hardware_optimizations.gate_times.get(&gate_name) {
total_time += time;
} else {
total_time += 1e-6;
}
}
total_time
}
#[must_use]
pub fn are_qubits_connected(&self, qubit1: usize, qubit2: usize) -> bool {
if qubit1 >= self.num_qubits() || qubit2 >= self.num_qubits() {
return false;
}
self.hardware_optimizations.connectivity_graph[[qubit1, qubit2]]
}
#[must_use]
pub fn get_decoherence_time(&self, qubit: usize) -> Option<f64> {
self.hardware_optimizations
.decoherence_times
.get(qubit)
.copied()
}
pub fn with_parameters(&self, parameters: Array1<f64>) -> Result<Self, String> {
let mut new_circuit = self.clone();
new_circuit.update_parameters(parameters)?;
Ok(new_circuit)
}
fn gate_type_to_string(gate_type: &InterfaceGateType) -> String {
match gate_type {
InterfaceGateType::Identity => "I".to_string(),
InterfaceGateType::PauliX | InterfaceGateType::X => "X".to_string(),
InterfaceGateType::PauliY => "Y".to_string(),
InterfaceGateType::PauliZ => "Z".to_string(),
InterfaceGateType::Hadamard | InterfaceGateType::H => "H".to_string(),
InterfaceGateType::S => "S".to_string(),
InterfaceGateType::T => "T".to_string(),
InterfaceGateType::Phase(_) => "Phase".to_string(),
InterfaceGateType::RX(_) => "RX".to_string(),
InterfaceGateType::RY(_) => "RY".to_string(),
InterfaceGateType::RZ(_) => "RZ".to_string(),
InterfaceGateType::U1(_) => "U1".to_string(),
InterfaceGateType::U2(_, _) => "U2".to_string(),
InterfaceGateType::U3(_, _, _) => "U3".to_string(),
InterfaceGateType::CNOT => "CNOT".to_string(),
InterfaceGateType::CZ => "CZ".to_string(),
InterfaceGateType::CY => "CY".to_string(),
InterfaceGateType::SWAP => "SWAP".to_string(),
InterfaceGateType::ISwap => "ISwap".to_string(),
InterfaceGateType::CRX(_) => "CRX".to_string(),
InterfaceGateType::CRY(_) => "CRY".to_string(),
InterfaceGateType::CRZ(_) => "CRZ".to_string(),
InterfaceGateType::CPhase(_) => "CPhase".to_string(),
InterfaceGateType::Toffoli => "Toffoli".to_string(),
InterfaceGateType::Fredkin => "Fredkin".to_string(),
InterfaceGateType::MultiControlledX(_) => "MCX".to_string(),
InterfaceGateType::MultiControlledZ(_) => "MCZ".to_string(),
InterfaceGateType::Custom(name, _) => name.clone(),
InterfaceGateType::Measure => "Measure".to_string(),
InterfaceGateType::Reset => "Reset".to_string(),
}
}
}
impl HardwareOptimizations {
#[must_use]
pub fn for_hardware(architecture: HardwareArchitecture, num_qubits: usize) -> Self {
let connectivity_graph = match architecture {
HardwareArchitecture::Superconducting => {
let mut graph = Array2::from_elem((num_qubits, num_qubits), false);
for i in 0..num_qubits.saturating_sub(1) {
graph[[i, i + 1]] = true;
graph[[i + 1, i]] = true;
}
graph
}
HardwareArchitecture::TrappedIon => {
Array2::from_elem((num_qubits, num_qubits), true)
}
HardwareArchitecture::Photonic => {
let mut graph = Array2::from_elem((num_qubits, num_qubits), false);
for i in 0..num_qubits {
for j in 0..num_qubits {
if (i as i32 - j as i32).abs() <= 2 {
graph[[i, j]] = true;
}
}
}
graph
}
_ => Array2::from_elem((num_qubits, num_qubits), true),
};
let gate_fidelities = match architecture {
HardwareArchitecture::Superconducting => {
let mut fidelities = HashMap::new();
fidelities.insert("X".to_string(), 0.999);
fidelities.insert("Y".to_string(), 0.999);
fidelities.insert("Z".to_string(), 0.9999);
fidelities.insert("H".to_string(), 0.998);
fidelities.insert("CNOT".to_string(), 0.995);
fidelities.insert("CZ".to_string(), 0.996);
fidelities
}
HardwareArchitecture::TrappedIon => {
let mut fidelities = HashMap::new();
fidelities.insert("X".to_string(), 0.9999);
fidelities.insert("Y".to_string(), 0.9999);
fidelities.insert("Z".to_string(), 0.99999);
fidelities.insert("H".to_string(), 0.9999);
fidelities.insert("CNOT".to_string(), 0.999);
fidelities.insert("CZ".to_string(), 0.999);
fidelities
}
_ => {
let mut fidelities = HashMap::new();
fidelities.insert("X".to_string(), 0.99);
fidelities.insert("Y".to_string(), 0.99);
fidelities.insert("Z".to_string(), 0.999);
fidelities.insert("H".to_string(), 0.99);
fidelities.insert("CNOT".to_string(), 0.98);
fidelities.insert("CZ".to_string(), 0.98);
fidelities
}
};
let decoherence_times = match architecture {
HardwareArchitecture::Superconducting => {
Array1::from_vec(vec![50e-6; num_qubits]) }
HardwareArchitecture::TrappedIon => {
Array1::from_vec(vec![100e-3; num_qubits]) }
_ => Array1::from_vec(vec![10e-6; num_qubits]),
};
let gate_times = match architecture {
HardwareArchitecture::Superconducting => {
let mut times = HashMap::new();
times.insert("X".to_string(), 20e-9);
times.insert("Y".to_string(), 20e-9);
times.insert("Z".to_string(), 0.0);
times.insert("H".to_string(), 20e-9);
times.insert("CNOT".to_string(), 40e-9);
times.insert("CZ".to_string(), 40e-9);
times
}
HardwareArchitecture::TrappedIon => {
let mut times = HashMap::new();
times.insert("X".to_string(), 10e-6);
times.insert("Y".to_string(), 10e-6);
times.insert("Z".to_string(), 0.0);
times.insert("H".to_string(), 10e-6);
times.insert("CNOT".to_string(), 100e-6);
times.insert("CZ".to_string(), 100e-6);
times
}
_ => {
let mut times = HashMap::new();
times.insert("X".to_string(), 1e-6);
times.insert("Y".to_string(), 1e-6);
times.insert("Z".to_string(), 0.0);
times.insert("H".to_string(), 1e-6);
times.insert("CNOT".to_string(), 10e-6);
times.insert("CZ".to_string(), 10e-6);
times
}
};
let crosstalk_matrix = Array2::zeros((num_qubits, num_qubits));
Self {
connectivity_graph,
gate_fidelities,
decoherence_times,
gate_times,
crosstalk_matrix,
}
}
pub fn set_gate_fidelity(&mut self, gate_name: &str, fidelity: f64) {
self.gate_fidelities.insert(gate_name.to_string(), fidelity);
}
pub fn set_gate_time(&mut self, gate_name: &str, time: f64) {
self.gate_times.insert(gate_name.to_string(), time);
}
pub fn set_decoherence_time(&mut self, qubit: usize, time: f64) {
if qubit < self.decoherence_times.len() {
self.decoherence_times[qubit] = time;
}
}
pub fn set_connectivity(&mut self, qubit1: usize, qubit2: usize, connected: bool) {
if qubit1 < self.connectivity_graph.nrows() && qubit2 < self.connectivity_graph.ncols() {
self.connectivity_graph[[qubit1, qubit2]] = connected;
self.connectivity_graph[[qubit2, qubit1]] = connected; }
}
#[must_use]
pub fn average_gate_fidelity(&self) -> f64 {
let fidelities: Vec<f64> = self.gate_fidelities.values().copied().collect();
if fidelities.is_empty() {
1.0
} else {
fidelities.iter().sum::<f64>() / fidelities.len() as f64
}
}
#[must_use]
pub fn connectivity_degree(&self, qubit: usize) -> usize {
if qubit >= self.connectivity_graph.nrows() {
return 0;
}
self.connectivity_graph
.row(qubit)
.iter()
.map(|&x| usize::from(x))
.sum()
}
}