use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
pub type CircuitBuilder<const N: usize> = Circuit<N>;
use quantrs2_core::{
decomposition::{utils as decomp_utils, CompositeGate},
error::QuantRS2Result,
gate::{
multi::{
Fredkin,
ISwap,
Toffoli,
CH,
CNOT,
CRX,
CRY,
CRZ,
CS,
CY,
CZ,
DCX,
ECR,
RXX,
RYY,
RZX,
RZZ,
SWAP,
},
single::{
Hadamard,
Identity,
PGate,
PauliX,
PauliY,
PauliZ,
Phase,
PhaseDagger,
RotationX,
RotationY,
RotationZ,
SqrtX,
SqrtXDagger,
TDagger,
UGate,
T,
},
GateOp,
},
qubit::QubitId,
register::Register,
};
use scirs2_core::Complex64;
use std::any::Any;
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct CircuitStats {
pub total_gates: usize,
pub gate_counts: HashMap<String, usize>,
pub depth: usize,
pub two_qubit_gates: usize,
pub multi_qubit_gates: usize,
pub gate_density: f64,
pub used_qubits: usize,
pub total_qubits: usize,
}
#[derive(Debug, Clone)]
pub struct GatePool {
gates: HashMap<String, Arc<dyn GateOp + Send + Sync>>,
}
impl GatePool {
#[must_use]
pub fn new() -> Self {
let mut gates = HashMap::with_capacity(16);
for qubit_id in 0..32 {
let qubit = QubitId::new(qubit_id);
gates.insert(
format!("H_{qubit_id}"),
Arc::new(Hadamard { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
);
gates.insert(
format!("X_{qubit_id}"),
Arc::new(PauliX { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
);
gates.insert(
format!("Y_{qubit_id}"),
Arc::new(PauliY { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
);
gates.insert(
format!("Z_{qubit_id}"),
Arc::new(PauliZ { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
);
gates.insert(
format!("S_{qubit_id}"),
Arc::new(Phase { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
);
gates.insert(
format!("T_{qubit_id}"),
Arc::new(T { target: qubit }) as Arc<dyn GateOp + Send + Sync>,
);
}
Self { gates }
}
pub fn get_gate<G: GateOp + Clone + Send + Sync + 'static>(
&mut self,
gate: G,
) -> Arc<dyn GateOp + Send + Sync> {
if gate.is_parameterized() {
return Arc::new(gate) as Arc<dyn GateOp + Send + Sync>;
}
let key = format!("{}_{:?}", gate.name(), gate.qubits());
if let Some(cached_gate) = self.gates.get(&key) {
cached_gate.clone()
} else {
let arc_gate = Arc::new(gate) as Arc<dyn GateOp + Send + Sync>;
self.gates.insert(key, arc_gate.clone());
arc_gate
}
}
}
impl Default for GatePool {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Measure {
pub target: QubitId,
}
impl GateOp for Measure {
fn name(&self) -> &'static str {
"measure"
}
fn qubits(&self) -> Vec<QubitId> {
vec![self.target]
}
fn is_parameterized(&self) -> bool {
false
}
fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
Ok(vec![
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(1.0, 0.0),
])
}
fn as_any(&self) -> &dyn Any {
self
}
fn clone_gate(&self) -> Box<dyn GateOp> {
Box::new(self.clone())
}
}
struct BoxGateWrapper(Box<dyn GateOp>);
impl std::fmt::Debug for BoxGateWrapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
unsafe impl Send for BoxGateWrapper {}
unsafe impl Sync for BoxGateWrapper {}
impl GateOp for BoxGateWrapper {
fn name(&self) -> &'static str {
self.0.name()
}
fn qubits(&self) -> Vec<QubitId> {
self.0.qubits()
}
fn num_qubits(&self) -> usize {
self.0.num_qubits()
}
fn is_parameterized(&self) -> bool {
self.0.is_parameterized()
}
fn matrix(&self) -> QuantRS2Result<Vec<Complex64>> {
self.0.matrix()
}
fn as_any(&self) -> &dyn std::any::Any {
self.0.as_any()
}
fn clone_gate(&self) -> Box<dyn GateOp> {
self.0.clone_gate()
}
}
#[derive(Debug, Clone)]
pub struct BarrierInfo {
pub after_gate_index: usize,
pub qubits: Vec<QubitId>,
}
pub struct Circuit<const N: usize> {
gates: Vec<Arc<dyn GateOp + Send + Sync>>,
gate_pool: GatePool,
pub barriers: Vec<BarrierInfo>,
}
impl<const N: usize> Clone for Circuit<N> {
fn clone(&self) -> Self {
Self {
gates: self.gates.clone(),
gate_pool: self.gate_pool.clone(),
barriers: self.barriers.clone(),
}
}
}
impl<const N: usize> fmt::Debug for Circuit<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Circuit")
.field("num_qubits", &N)
.field("num_gates", &self.gates.len())
.finish()
}
}
impl<const N: usize> Circuit<N> {
#[must_use]
pub fn new() -> Self {
Self {
gates: Vec::with_capacity(64), gate_pool: GatePool::new(),
barriers: Vec::new(),
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
gates: Vec::with_capacity(capacity),
gate_pool: GatePool::new(),
barriers: Vec::new(),
}
}
pub fn add_gate<G: GateOp + Clone + Send + Sync + 'static>(
&mut self,
gate: G,
) -> QuantRS2Result<&mut Self> {
for qubit in gate.qubits() {
if qubit.id() as usize >= N {
return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
"Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
gate.name(),
qubit.id(),
N,
N - 1
)));
}
}
let gate_arc = self.gate_pool.get_gate(gate);
self.gates.push(gate_arc);
Ok(self)
}
pub fn from_gates(gates: Vec<Box<dyn GateOp>>) -> QuantRS2Result<Self> {
let mut circuit = Self::with_capacity(gates.len());
for gate in gates {
let in_range = gate.qubits().iter().all(|q| (q.id() as usize) < N);
if in_range {
let arc: Arc<dyn GateOp + Send + Sync> = Arc::new(BoxGateWrapper(gate));
circuit.gates.push(arc);
}
}
Ok(circuit)
}
pub fn add_gate_arc(
&mut self,
gate: Arc<dyn GateOp + Send + Sync>,
) -> QuantRS2Result<&mut Self> {
for qubit in gate.qubits() {
if qubit.id() as usize >= N {
return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
"Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
gate.name(),
qubit.id(),
N,
N - 1
)));
}
}
self.gates.push(gate);
Ok(self)
}
#[must_use]
pub fn gates(&self) -> &[Arc<dyn GateOp + Send + Sync>] {
&self.gates
}
#[must_use]
pub fn gates_as_boxes(&self) -> Vec<Box<dyn GateOp>> {
self.gates
.iter()
.map(|arc_gate| arc_gate.clone_gate())
.collect()
}
#[must_use]
pub fn count_gates_by_type(&self) -> HashMap<String, usize> {
let mut counts = HashMap::new();
for gate in &self.gates {
*counts.entry(gate.name().to_string()).or_insert(0) += 1;
}
counts
}
#[must_use]
pub fn calculate_depth(&self) -> usize {
if self.gates.is_empty() {
return 0;
}
let mut qubit_last_used = vec![0; N];
let mut max_depth = 0;
for (gate_idx, gate) in self.gates.iter().enumerate() {
let gate_qubits = gate.qubits();
let gate_start_depth = gate_qubits
.iter()
.map(|q| qubit_last_used[q.id() as usize])
.max()
.unwrap_or(0);
let gate_end_depth = gate_start_depth + 1;
for qubit in gate_qubits {
qubit_last_used[qubit.id() as usize] = gate_end_depth;
}
max_depth = max_depth.max(gate_end_depth);
}
max_depth
}
#[must_use]
pub fn count_two_qubit_gates(&self) -> usize {
self.gates
.iter()
.filter(|gate| gate.qubits().len() == 2)
.count()
}
#[must_use]
pub fn count_multi_qubit_gates(&self) -> usize {
self.gates
.iter()
.filter(|gate| gate.qubits().len() >= 3)
.count()
}
#[must_use]
pub fn calculate_critical_path(&self) -> usize {
self.calculate_depth()
}
#[must_use]
pub fn calculate_gate_density(&self) -> f64 {
if N == 0 {
0.0
} else {
self.gates.len() as f64 / N as f64
}
}
#[must_use]
pub fn get_used_qubits(&self) -> HashSet<QubitId> {
let mut used_qubits = HashSet::new();
for gate in &self.gates {
for qubit in gate.qubits() {
used_qubits.insert(qubit);
}
}
used_qubits
}
#[must_use]
pub fn uses_all_qubits(&self) -> bool {
self.get_used_qubits().len() == N
}
#[must_use]
pub fn gates_on_qubit(&self, target_qubit: QubitId) -> Vec<&Arc<dyn GateOp + Send + Sync>> {
self.gates
.iter()
.filter(|gate| gate.qubits().contains(&target_qubit))
.collect()
}
#[must_use]
pub fn gates_in_range(&self, start: usize, end: usize) -> &[Arc<dyn GateOp + Send + Sync>] {
let end = end.min(self.gates.len().saturating_sub(1));
let start = start.min(end);
&self.gates[start..=end]
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.gates.is_empty()
}
#[must_use]
pub fn get_stats(&self) -> CircuitStats {
let gate_counts = self.count_gates_by_type();
let depth = self.calculate_depth();
let two_qubit_gates = self.count_two_qubit_gates();
let multi_qubit_gates = self.count_multi_qubit_gates();
let gate_density = self.calculate_gate_density();
let used_qubits = self.get_used_qubits().len();
CircuitStats {
total_gates: self.gates.len(),
gate_counts,
depth,
two_qubit_gates,
multi_qubit_gates,
gate_density,
used_qubits,
total_qubits: N,
}
}
#[must_use]
pub const fn num_qubits(&self) -> usize {
N
}
#[must_use]
pub fn num_gates(&self) -> usize {
self.gates.len()
}
#[must_use]
pub fn get_gate_names(&self) -> Vec<String> {
self.gates
.iter()
.map(|gate| gate.name().to_string())
.collect()
}
pub(crate) fn find_gate_by_type_and_index(
&self,
gate_type: &str,
index: usize,
) -> Option<&dyn GateOp> {
let mut count = 0;
for gate in &self.gates {
if gate.name() == gate_type {
if count == index {
return Some(gate.as_ref());
}
count += 1;
}
}
None
}
pub fn run<S: Simulator<N>>(&self, simulator: S) -> QuantRS2Result<Register<N>> {
simulator.run(self)
}
pub fn decompose(&self) -> QuantRS2Result<Self> {
let mut decomposed = Self::new();
let boxed_gates = self.gates_as_boxes();
let simple_gates = decomp_utils::decompose_circuit(&boxed_gates)?;
for gate in simple_gates {
decomposed.add_gate_box(gate)?;
}
Ok(decomposed)
}
#[must_use]
pub const fn build(self) -> Self {
self
}
pub fn optimize(&self) -> QuantRS2Result<Self> {
let mut optimized = Self::new();
let boxed_gates = self.gates_as_boxes();
let simplified_gates_result = decomp_utils::optimize_gate_sequence(&boxed_gates);
if let Ok(simplified_gates) = simplified_gates_result {
for g in simplified_gates {
optimized.add_gate_box(g)?;
}
}
let new_gate_count = optimized.gates.len();
optimized.barriers = self
.barriers
.iter()
.map(|b| BarrierInfo {
after_gate_index: b.after_gate_index.min(new_gate_count),
qubits: b.qubits.clone(),
})
.collect();
Ok(optimized)
}
pub(crate) fn add_gate_box(&mut self, gate: Box<dyn GateOp>) -> QuantRS2Result<&mut Self> {
for qubit in gate.qubits() {
if qubit.id() as usize >= N {
return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
"Gate '{}' targets qubit {} which is out of range for {}-qubit circuit (valid range: 0-{})",
gate.name(),
qubit.id(),
N,
N - 1
)));
}
}
let cloned_gate = gate.clone_gate();
if let Some(g) = cloned_gate.as_any().downcast_ref::<Hadamard>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<PauliX>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<PauliY>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<PauliZ>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<CNOT>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<CZ>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<SWAP>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<CY>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<CH>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<CS>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<Toffoli>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<Fredkin>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<CRX>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<CRY>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<CRZ>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<ISwap>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<ECR>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<RXX>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<RYY>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<RZZ>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<RZX>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<DCX>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<RotationX>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<RotationY>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<RotationZ>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<Phase>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<PhaseDagger>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<T>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<TDagger>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<SqrtX>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<SqrtXDagger>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<UGate>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<PGate>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<Identity>() {
self.gates
.push(Arc::new(*g) as Arc<dyn GateOp + Send + Sync>);
} else if let Some(g) = cloned_gate.as_any().downcast_ref::<Measure>() {
self.gates
.push(Arc::new(g.clone()) as Arc<dyn GateOp + Send + Sync>);
} else {
self.gates
.push(Arc::new(BoxGateWrapper(cloned_gate)) as Arc<dyn GateOp + Send + Sync>);
}
Ok(self)
}
pub fn create_composite(
&self,
start_idx: usize,
end_idx: usize,
name: &str,
) -> QuantRS2Result<CompositeGate> {
if start_idx >= self.gates.len() || end_idx > self.gates.len() || start_idx >= end_idx {
return Err(quantrs2_core::error::QuantRS2Error::InvalidInput(format!(
"Invalid start/end indices ({}/{}) for circuit with {} gates",
start_idx,
end_idx,
self.gates.len()
)));
}
let mut gates: Vec<Box<dyn GateOp>> = Vec::new();
for gate in &self.gates[start_idx..end_idx] {
gates.push(decomp_utils::clone_gate(gate.as_ref())?);
}
let mut qubits = Vec::new();
for gate in &gates {
for qubit in gate.qubits() {
if !qubits.contains(&qubit) {
qubits.push(qubit);
}
}
}
Ok(CompositeGate {
gates,
qubits,
name: name.to_string(),
})
}
pub fn add_composite(&mut self, composite: &CompositeGate) -> QuantRS2Result<&mut Self> {
for gate in &composite.gates {
let gate_clone = decomp_utils::clone_gate(gate.as_ref())?;
self.add_gate_box(gate_clone)?;
}
Ok(self)
}
#[must_use]
pub fn with_classical_control(self) -> crate::classical::ClassicalCircuit<N> {
let mut classical_circuit = crate::classical::ClassicalCircuit::new();
let _ = classical_circuit.add_classical_register("c", N);
for gate in self.gates {
let boxed_gate = gate.clone_gate();
classical_circuit
.operations
.push(crate::classical::CircuitOp::Quantum(boxed_gate));
}
classical_circuit
}
pub fn bell_state(&mut self, qubit1: u32, qubit2: u32) -> QuantRS2Result<&mut Self> {
self.h(QubitId::new(qubit1))?;
self.cnot(QubitId::new(qubit1), QubitId::new(qubit2))?;
Ok(self)
}
pub fn ghz_state(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
if qubits.is_empty() {
return Ok(self);
}
self.h(QubitId::new(qubits[0]))?;
for i in 1..qubits.len() {
self.cnot(QubitId::new(qubits[0]), QubitId::new(qubits[i]))?;
}
Ok(self)
}
pub fn w_state(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
if qubits.is_empty() {
return Ok(self);
}
let n = qubits.len() as f64;
self.ry(QubitId::new(qubits[0]), 2.0 * (1.0 / n.sqrt()).acos())?;
for i in 1..qubits.len() {
let angle = 2.0 * (1.0 / (n - i as f64).sqrt()).acos();
self.cry(QubitId::new(qubits[i - 1]), QubitId::new(qubits[i]), angle)?;
}
for i in 0..qubits.len() - 1 {
self.cnot(QubitId::new(qubits[i + 1]), QubitId::new(qubits[i]))?;
}
Ok(self)
}
pub fn plus_state_all(&mut self) -> QuantRS2Result<&mut Self> {
for i in 0..N {
self.h(QubitId::new(i as u32))?;
}
Ok(self)
}
pub fn cnot_ladder(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
if qubits.len() < 2 {
return Ok(self);
}
for i in 0..qubits.len() - 1 {
self.cnot(QubitId::new(qubits[i]), QubitId::new(qubits[i + 1]))?;
}
Ok(self)
}
pub fn cnot_ring(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
if qubits.len() < 2 {
return Ok(self);
}
self.cnot_ladder(qubits)?;
let last_idx = qubits.len() - 1;
self.cnot(QubitId::new(qubits[last_idx]), QubitId::new(qubits[0]))?;
Ok(self)
}
pub fn swap_ladder(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
if qubits.len() < 2 {
return Ok(self);
}
for i in 0..qubits.len() - 1 {
self.swap(QubitId::new(qubits[i]), QubitId::new(qubits[i + 1]))?;
}
Ok(self)
}
pub fn cz_ladder(&mut self, qubits: &[u32]) -> QuantRS2Result<&mut Self> {
if qubits.len() < 2 {
return Ok(self);
}
for i in 0..qubits.len() - 1 {
self.cz(QubitId::new(qubits[i]), QubitId::new(qubits[i + 1]))?;
}
Ok(self)
}
}
impl<const N: usize> Default for Circuit<N> {
fn default() -> Self {
Self::new()
}
}
pub trait Simulator<const N: usize> {
fn run(&self, circuit: &Circuit<N>) -> QuantRS2Result<Register<N>>;
}
#[cfg(test)]
mod tests;