diff options
| author | hachem <im@hachem.wtf> | 2026-08-24 14:48:36 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-24 14:48:36 +0200 |
| commit | 24d639224ca11112025289065d7538851606b56e (patch) | |
| tree | e7e26c89cd20099f03ff32727ba0af2cf4762088 /src/core/circuit.rs | |
| parent | 548dc42d9f454cb8c29fddb88ca41f8b1b595882 (diff) | |
[chore]: unwrap project
Diffstat (limited to 'src/core/circuit.rs')
| -rw-r--r-- | src/core/circuit.rs | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/src/core/circuit.rs b/src/core/circuit.rs new file mode 100644 index 0000000..6647f58 --- /dev/null +++ b/src/core/circuit.rs @@ -0,0 +1,477 @@ +use super::{CustomGate, QuantumState, Runtime, RuntimeConfig}; +use crate::{format_amplitude, format_probability, Vector}; +use core::fmt; +use std::sync::Arc; + +#[derive(Clone)] +pub enum GateOp { + H(usize), + X(usize), + Y(usize), + Z(usize), + S(usize), + T(usize), + Sdg(usize), + Tdg(usize), + Sx(usize), + Sxdg(usize), + Rx(usize, f64), + Ry(usize, f64), + Rz(usize, f64), + P(usize, f64), + U1(usize, f64), + U2(usize, f64, f64), + U3(usize, f64, f64, f64), + CNOT(usize, usize), + CZ(usize, usize), + SWAP(usize, usize), + CRx(usize, usize, f64), + CRy(usize, usize, f64), + CRz(usize, usize, f64), + CP(usize, usize, f64), + CCNOT(usize, usize, usize), + CSWAP(usize, usize, usize), + Measure(usize, usize), + Custom(Arc<CustomGate>, Vec<usize>), +} + +impl GateOp { + pub fn name(&self) -> &str { + match self { + GateOp::H(_) => "H", + GateOp::X(_) => "X", + GateOp::Y(_) => "Y", + GateOp::Z(_) => "Z", + GateOp::S(_) => "S", + GateOp::T(_) => "T", + GateOp::Sdg(_) => "S†", + GateOp::Tdg(_) => "T†", + GateOp::Sx(_) => "√X", + GateOp::Sxdg(_) => "√X†", + GateOp::Rx(_, _) => "Rx", + GateOp::Ry(_, _) => "Ry", + GateOp::Rz(_, _) => "Rz", + GateOp::P(_, _) => "P", + GateOp::U1(_, _) => "U1", + GateOp::U2(_, _, _) => "U2", + GateOp::U3(_, _, _, _) => "U3", + GateOp::CRx(_, _, _) => "CRx", + GateOp::CRy(_, _, _) => "CRy", + GateOp::CRz(_, _, _) => "CRz", + GateOp::CP(_, _, _) => "CP", + GateOp::CNOT(_, _) => "CNOT", + GateOp::CZ(_, _) => "CZ", + GateOp::SWAP(_, _) => "SWAP", + GateOp::CCNOT(_, _, _) => "CCNOT", + GateOp::CSWAP(_, _, _) => "CSWAP", + GateOp::Measure(_, _) => "M", + GateOp::Custom(gate, _) => &gate.name, + } + } + + pub fn quantum_targets(&self) -> Vec<usize> { + match self { + GateOp::H(t) + | GateOp::X(t) + | GateOp::Y(t) + | GateOp::Z(t) + | GateOp::S(t) + | GateOp::T(t) + | GateOp::Sdg(t) + | GateOp::Tdg(t) + | GateOp::Sx(t) + | GateOp::Sxdg(t) + | GateOp::Rx(t, _) + | GateOp::Ry(t, _) + | GateOp::Rz(t, _) + | GateOp::P(t, _) + | GateOp::U1(t, _) + | GateOp::U2(t, _, _) + | GateOp::U3(t, _, _, _) => vec![*t], + GateOp::CNOT(c, t) + | GateOp::CZ(c, t) + | GateOp::SWAP(c, t) + | GateOp::CRx(c, t, _) + | GateOp::CRy(c, t, _) + | GateOp::CRz(c, t, _) + | GateOp::CP(c, t, _) => vec![*c, *t], + GateOp::CCNOT(c1, c2, t) | GateOp::CSWAP(c1, c2, t) => vec![*c1, *c2, *t], + GateOp::Measure(q, _) => vec![*q], + GateOp::Custom(_, targets) => targets.clone(), + } + } + + pub fn classical_targets(&self) -> Vec<usize> { + match self { + GateOp::Measure(_, c) => vec![*c], + _ => vec![], + } + } + + pub fn is_measurement(&self) -> bool { + matches!(self, GateOp::Measure(_, _)) + } + + pub fn is_custom(&self) -> bool { + matches!(self, GateOp::Custom(_, _)) + } + + pub fn is_non_clifford(&self) -> bool { + matches!( + self, + GateOp::T(_) + | GateOp::Tdg(_) + | GateOp::Sx(_) + | GateOp::Sxdg(_) + | GateOp::Rx(_, _) + | GateOp::Ry(_, _) + | GateOp::Rz(_, _) + | GateOp::P(_, _) + | GateOp::U1(_, _) + | GateOp::U2(_, _, _) + | GateOp::U3(_, _, _, _) + | GateOp::CRx(_, _, _) + | GateOp::CRy(_, _, _) + | GateOp::CRz(_, _, _) + | GateOp::CP(_, _, _) + ) + } +} + +pub struct QuantumCircuit { + num_qubits: usize, + num_classical: usize, + operations: Vec<GateOp>, + computed_state: Option<QuantumState>, +} + +impl QuantumCircuit { + pub fn new(num_qubits: usize) -> QuantumCircuit { + QuantumCircuit { + num_qubits, + num_classical: 0, + operations: Vec::new(), + computed_state: None, + } + } + + pub fn with_classical(num_qubits: usize, num_classical: usize) -> QuantumCircuit { + QuantumCircuit { + num_qubits, + num_classical, + operations: Vec::new(), + computed_state: None, + } + } + + pub fn num_qubits(&self) -> usize { + self.num_qubits + } + + pub fn num_classical(&self) -> usize { + self.num_classical + } + + pub fn operations(&self) -> &[GateOp] { + &self.operations + } + + pub fn is_computed(&self) -> bool { + self.computed_state.is_some() + } + + pub fn compute(&mut self) -> &QuantumState { + self.compute_with(Runtime::default()) + } + + pub fn compute_with(&mut self, runtime: Runtime) -> &QuantumState { + if self.computed_state.is_none() { + self.computed_state = Some(runtime.compute(self.num_qubits, &self.operations)); + } + self.computed_state.as_ref().unwrap() + } + + pub fn compute_with_config(&mut self, config: RuntimeConfig) -> &QuantumState { + if self.computed_state.is_none() { + self.computed_state = Some(config.compute(self.num_qubits, &self.operations)); + } + self.computed_state.as_ref().unwrap() + } + + pub fn state(&mut self) -> &QuantumState { + self.compute() + } + + pub fn state_with(&mut self, runtime: Runtime) -> &QuantumState { + self.compute_with(runtime) + } + + pub fn state_with_config(&mut self, config: RuntimeConfig) -> &QuantumState { + self.compute_with_config(config) + } + + pub fn h(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::H(target)); + self.computed_state = None; + self + } + + pub fn x(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::X(target)); + self.computed_state = None; + self + } + + pub fn y(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::Y(target)); + self.computed_state = None; + self + } + + pub fn z(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::Z(target)); + self.computed_state = None; + self + } + + pub fn s(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::S(target)); + self.computed_state = None; + self + } + + pub fn t(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::T(target)); + self.computed_state = None; + self + } + + pub fn sdg(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::Sdg(target)); + self.computed_state = None; + self + } + + pub fn tdg(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::Tdg(target)); + self.computed_state = None; + self + } + + pub fn sx(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::Sx(target)); + self.computed_state = None; + self + } + + pub fn sxdg(&mut self, target: usize) -> &mut Self { + self.operations.push(GateOp::Sxdg(target)); + self.computed_state = None; + self + } + + pub fn rx(&mut self, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::Rx(target, theta)); + self.computed_state = None; + self + } + + pub fn ry(&mut self, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::Ry(target, theta)); + self.computed_state = None; + self + } + + pub fn rz(&mut self, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::Rz(target, theta)); + self.computed_state = None; + self + } + + pub fn p(&mut self, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::P(target, theta)); + self.computed_state = None; + self + } + + pub fn u1(&mut self, target: usize, lambda: f64) -> &mut Self { + self.operations.push(GateOp::U1(target, lambda)); + self.computed_state = None; + self + } + + pub fn u2(&mut self, target: usize, phi: f64, lambda: f64) -> &mut Self { + self.operations.push(GateOp::U2(target, phi, lambda)); + self.computed_state = None; + self + } + + pub fn u3(&mut self, target: usize, theta: f64, phi: f64, lambda: f64) -> &mut Self { + self.operations.push(GateOp::U3(target, theta, phi, lambda)); + self.computed_state = None; + self + } + + pub fn crx(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::CRx(control, target, theta)); + self.computed_state = None; + self + } + + pub fn cry(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::CRy(control, target, theta)); + self.computed_state = None; + self + } + + pub fn crz(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::CRz(control, target, theta)); + self.computed_state = None; + self + } + + pub fn cp(&mut self, control: usize, target: usize, theta: f64) -> &mut Self { + self.operations.push(GateOp::CP(control, target, theta)); + self.computed_state = None; + self + } + + pub fn cnot(&mut self, control: usize, target: usize) -> &mut Self { + self.operations.push(GateOp::CNOT(control, target)); + self.computed_state = None; + self + } + + pub fn cx(&mut self, control: usize, target: usize) -> &mut Self { + self.cnot(control, target) + } + + pub fn cz(&mut self, control: usize, target: usize) -> &mut Self { + self.operations.push(GateOp::CZ(control, target)); + self.computed_state = None; + self + } + + pub fn swap(&mut self, qubit1: usize, qubit2: usize) -> &mut Self { + self.operations.push(GateOp::SWAP(qubit1, qubit2)); + self.computed_state = None; + self + } + + pub fn ccnot(&mut self, control1: usize, control2: usize, target: usize) -> &mut Self { + self.operations + .push(GateOp::CCNOT(control1, control2, target)); + self.computed_state = None; + self + } + + pub fn toffoli(&mut self, control1: usize, control2: usize, target: usize) -> &mut Self { + self.ccnot(control1, control2, target) + } + + pub fn cswap(&mut self, control: usize, target1: usize, target2: usize) -> &mut Self { + self.operations + .push(GateOp::CSWAP(control, target1, target2)); + self.computed_state = None; + self + } + + pub fn fredkin(&mut self, control: usize, target1: usize, target2: usize) -> &mut Self { + self.cswap(control, target1, target2) + } + + pub fn measure(&mut self, qubit: usize, classical: usize) -> &mut Self { + if classical >= self.num_classical { + self.num_classical = classical + 1; + } + self.operations.push(GateOp::Measure(qubit, classical)); + self + } + + pub fn measure_all(&mut self) -> &mut Self { + for i in 0..self.num_qubits { + self.measure(i, i); + } + self + } + + pub fn custom(&mut self, gate: &Arc<CustomGate>, targets: &[usize]) -> &mut Self { + self.operations + .push(GateOp::Custom(Arc::clone(gate), targets.to_vec())); + self.computed_state = None; + self + } + + pub fn apply_custom(&mut self, gate: CustomGate, targets: &[usize]) -> &mut Self { + self.operations + .push(GateOp::Custom(Arc::new(gate), targets.to_vec())); + self.computed_state = None; + self + } + + pub fn reset(&mut self) -> &mut Self { + self.operations.clear(); + self.computed_state = None; + self + } + + pub fn probability(&mut self, state_index: usize) -> f64 { + self.compute(); + let state = self.computed_state.as_ref().unwrap(); + let amp = state.get(state_index); + amp.norm2() + } + + pub fn probabilities(&mut self) -> Vec<f64> { + self.compute(); + let n = 1 << self.num_qubits; + let state = self.computed_state.as_ref().unwrap(); + (0..n).map(|i| state.get(i).norm2()).collect() + } + + pub fn print_probabilities(&mut self) { + let probs = self.probabilities(); + let n = self.num_qubits; + println!("Probabilities:"); + for (i, p) in probs.iter().enumerate() { + if *p > 1e-10 { + let basis: String = format!("{:0width$b}", i, width = n); + println!(" |{}⟩: {}", basis, format_probability(*p)); + } + } + } +} + +impl fmt::Display for QuantumCircuit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "QuantumCircuit ({} qubits, {} classical)", + self.num_qubits, self.num_classical + )?; + writeln!(f, "Operations:")?; + for (i, op) in self.operations.iter().enumerate() { + match op { + GateOp::Measure(q, c) => writeln!(f, " {}: {} q{} → c{}", i, op.name(), q, c)?, + GateOp::Custom(gate, targets) => { + writeln!(f, " {}: [{}] on {:?}", i, gate.name, targets)? + } + _ => writeln!(f, " {}: {} on {:?}", i, op.name(), op.quantum_targets())?, + } + } + if let Some(state) = &self.computed_state { + writeln!(f, "State:")?; + let n = 1 << self.num_qubits; + for i in 0..n { + let amp = state.get(i); + if amp.real.abs() > 1e-10 || amp.imaginary.abs() > 1e-10 { + let basis: String = format!("{:0width$b}", i, width = self.num_qubits); + writeln!(f, " |{}⟩: {}", basis, format_amplitude(&))?; + } + } + } else { + writeln!(f, "State: (not computed)")?; + } + Ok(()) + } +} |
