aboutsummaryrefslogtreecommitdiff
path: root/src/core/circuit.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/circuit.rs')
-rw-r--r--src/core/circuit.rs477
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(&amp))?;
+ }
+ }
+ } else {
+ writeln!(f, "State: (not computed)")?;
+ }
+ Ok(())
+ }
+}