use crate::{
backend::backend::{Gate1, Gate2, QuantumBackend},
error::Result,
utils::circuit_viz::{CircuitRecorder, Operation},
};
#[derive(Debug, Clone, Copy)]
pub struct QuantumSystem<B: QuantumBackend> {
backend: B,
}
impl<B: QuantumBackend> QuantumSystem<B> {
pub fn new(backend: B) -> Self {
QuantumSystem { backend }
}
pub async fn h(&mut self, qubit: usize) -> Result<()> {
self.backend.apply_single_gate(qubit, Gate1::H).await
}
pub async fn x(&mut self, qubit: usize) -> Result<()> {
self.backend.apply_single_gate(qubit, Gate1::X).await
}
pub async fn y(&mut self, qubit: usize) -> Result<()> {
self.backend.apply_single_gate(qubit, Gate1::Y).await
}
pub async fn z(&mut self, qubit: usize) -> Result<()> {
self.backend.apply_single_gate(qubit, Gate1::Z).await
}
pub async fn s(&mut self, qubit: usize) -> Result<()> {
self.backend.apply_single_gate(qubit, Gate1::S).await
}
pub async fn t(&mut self, qubit: usize) -> Result<()> {
self.backend.apply_single_gate(qubit, Gate1::T).await
}
pub async fn s_dag(&mut self, qubit: usize) -> Result<()> {
self.backend.apply_single_gate(qubit, Gate1::SDag).await
}
pub async fn t_dag(&mut self, qubit: usize) -> Result<()> {
self.backend.apply_single_gate(qubit, Gate1::TDag).await
}
pub async fn rx(&mut self, qubit: usize, angle: f64) -> Result<()> {
self.backend
.apply_single_gate(qubit, Gate1::Rx(angle))
.await
}
pub async fn ry(&mut self, qubit: usize, angle: f64) -> Result<()> {
self.backend
.apply_single_gate(qubit, Gate1::Ry(angle))
.await
}
pub async fn rz(&mut self, qubit: usize, angle: f64) -> Result<()> {
self.backend
.apply_single_gate(qubit, Gate1::Rz(angle))
.await
}
pub async fn cnot(&mut self, control: usize, target: usize) -> Result<()> {
self.backend
.apply_two_gate(control, target, Gate2::CNOT)
.await
}
pub async fn cz(&mut self, control: usize, target: usize) -> Result<()> {
self.backend
.apply_two_gate(control, target, Gate2::CZ)
.await
}
pub async fn swap(&mut self, q1: usize, q2: usize) -> Result<()> {
self.backend.apply_two_gate(q1, q2, Gate2::SWAP).await
}
pub async fn cy(&mut self, control: usize, target: usize) -> Result<()> {
self.backend
.apply_two_gate(control, target, Gate2::CY)
.await
}
pub async fn measure(&mut self, qubit: usize) -> Result<u8> {
self.backend.measure(qubit).await
}
pub async fn create_bell_pair(&mut self, q1: usize, q2: usize) -> Result<()> {
self.backend.create_entanglement(q1, q2).await
}
pub async fn ccx(&mut self, control1: usize, control2: usize, target: usize) -> Result<()> {
self.h(target).await?;
self.cnot(control2, target).await?;
self.t_dag(target).await?;
self.cnot(control1, target).await?;
self.t(target).await?;
self.cnot(control2, target).await?;
self.t_dag(target).await?;
self.cnot(control1, target).await?;
self.t(control2).await?;
self.t(target).await?;
self.cnot(control1, control2).await?;
self.h(target).await?;
self.t(control1).await?;
self.t_dag(control2).await?;
self.cnot(control1, control2).await?;
Ok(())
}
pub fn qubit_count(&self) -> usize {
self.backend.qubit_count()
}
pub fn with_recording(self) -> RecordingQuantumSystem<B> {
RecordingQuantumSystem::new(self)
}
}
pub struct RecordingQuantumSystem<B: QuantumBackend> {
system: QuantumSystem<B>,
recorder: CircuitRecorder,
}
impl<B: QuantumBackend> RecordingQuantumSystem<B> {
pub fn new(system: QuantumSystem<B>) -> Self {
let n_qubits = system.qubit_count();
RecordingQuantumSystem {
system,
recorder: CircuitRecorder::new(n_qubits),
}
}
pub async fn h(&mut self, qubit: usize) -> Result<()> {
self.recorder.record_single(qubit, Gate1::H);
self.system.h(qubit).await
}
pub async fn x(&mut self, qubit: usize) -> Result<()> {
self.recorder.record_single(qubit, Gate1::X);
self.system.x(qubit).await
}
pub async fn s_dag(&mut self, qubit: usize) -> Result<()> {
self.recorder.record_single(qubit, Gate1::SDag);
self.system.s_dag(qubit).await
}
pub async fn t_dag(&mut self, qubit: usize) -> Result<()> {
self.recorder.record_single(qubit, Gate1::TDag);
self.system.t_dag(qubit).await
}
pub async fn y(&mut self, qubit: usize) -> Result<()> {
self.recorder.record_single(qubit, Gate1::Y);
self.system.y(qubit).await
}
pub async fn z(&mut self, qubit: usize) -> Result<()> {
self.recorder.record_single(qubit, Gate1::Z);
self.system.z(qubit).await
}
pub async fn s(&mut self, qubit: usize) -> Result<()> {
self.recorder.record_single(qubit, Gate1::S);
self.system.s(qubit).await
}
pub async fn t(&mut self, qubit: usize) -> Result<()> {
self.recorder.record_single(qubit, Gate1::T);
self.system.t(qubit).await
}
pub async fn rx(&mut self, qubit: usize, angle: f64) -> Result<()> {
self.recorder.record_single(qubit, Gate1::Rx(angle));
self.system.rx(qubit, angle).await
}
pub async fn ry(&mut self, qubit: usize, angle: f64) -> Result<()> {
self.recorder.record_single(qubit, Gate1::Ry(angle));
self.system.ry(qubit, angle).await
}
pub async fn rz(&mut self, qubit: usize, angle: f64) -> Result<()> {
self.recorder.record_single(qubit, Gate1::Rz(angle));
self.system.rz(qubit, angle).await
}
pub async fn cnot(&mut self, control: usize, target: usize) -> Result<()> {
self.recorder.record_two(control, target, Gate2::CNOT);
self.system.cnot(control, target).await
}
pub async fn cz(&mut self, control: usize, target: usize) -> Result<()> {
self.recorder.record_two(control, target, Gate2::CZ);
self.system.cz(control, target).await
}
pub async fn swap(&mut self, q1: usize, q2: usize) -> Result<()> {
self.recorder.record_two(q1, q2, Gate2::SWAP);
self.system.swap(q1, q2).await
}
pub async fn cy(&mut self, control: usize, target: usize) -> Result<()> {
self.recorder.record_two(control, target, Gate2::CY);
self.system.cy(control, target).await
}
pub async fn measure(&mut self, qubit: usize) -> Result<u8> {
self.recorder.record_measure(qubit);
self.system.measure(qubit).await
}
pub async fn create_bell_pair(&mut self, q1: usize, q2: usize) -> Result<()> {
self.recorder.record_single(q1, Gate1::H);
self.recorder.record_two(q1, q2, Gate2::CNOT);
self.system.create_bell_pair(q1, q2).await
}
pub async fn ccx(&mut self, control1: usize, control2: usize, target: usize) -> Result<()> {
self.system.ccx(control1, control2, target).await
}
pub fn qubit_count(&self) -> usize {
self.system.qubit_count()
}
pub fn print_circuit(&self) {
log::debug!("{}", self.recorder.to_ascii());
}
pub fn circuit_string(&self) -> String {
self.recorder.to_ascii()
}
pub fn to_qasm(&self) -> String {
let mut qasm = String::new();
qasm.push_str("OPENQASM 3.0;\n");
qasm.push_str("// Bit ordering: q[0] is the least significant bit\n");
qasm.push_str("// Measurement results read as: c[n-1]...c[1]c[0]\n\n");
qasm.push_str("include \"stdgates.inc\";\n\n");
qasm.push_str(&format!("qubit[{}] q;\n", self.system.qubit_count()));
qasm.push_str(&format!("bit[{}] c;\n\n", self.system.qubit_count()));
for op in &self.recorder.operations {
match op {
Operation::Single(qubit, gate) => {
let gate_str = match gate {
Gate1::H => format!("h q[{}];", qubit),
Gate1::X => format!("x q[{}];", qubit),
Gate1::Y => format!("y q[{}];", qubit),
Gate1::Z => format!("z q[{}];", qubit),
Gate1::S => format!("s q[{}];", qubit),
Gate1::SDag => format!("sdg q[{}];", qubit),
Gate1::T => format!("t q[{}];", qubit),
Gate1::TDag => format!("tdg q[{}];", qubit),
Gate1::Rx(angle) => {
format!("rx({}) q[{}];", Self::format_angle(*angle), qubit)
}
Gate1::Ry(angle) => {
format!("ry({}) q[{}];", Self::format_angle(*angle), qubit)
}
Gate1::Rz(angle) => {
format!("rz({}) q[{}];", Self::format_angle(*angle), qubit)
}
};
qasm.push_str(&gate_str);
qasm.push('\n');
}
Operation::Two(q1, q2, gate) => {
let gate_str = match gate {
Gate2::CNOT => format!("cx q[{}], q[{}];", q1, q2),
Gate2::CY => format!("cy q[{}], q[{}];", q1, q2),
Gate2::CZ => format!("cz q[{}], q[{}];", q1, q2),
Gate2::SWAP => format!("swap q[{}], q[{}];", q1, q2),
};
qasm.push_str(&gate_str);
qasm.push('\n');
}
Operation::Measure(qubit) => {
qasm.push_str(&format!("c[{}] = measure q[{}];\n", qubit, qubit));
}
}
}
qasm
}
fn format_angle(radians: f64) -> String {
use std::f64::consts::PI;
const FRACTIONS: &[(f64, &str)] = &[
(0.0, "0"),
(0.25, "pi/4"),
(0.5, "pi/2"),
(0.75, "3*pi/4"),
(1.0, "pi"),
(1.25, "5*pi/4"),
(1.5, "3*pi/2"),
(1.75, "7*pi/4"),
(2.0, "2*pi"),
(0.333333333, "pi/3"),
(0.666666667, "2*pi/3"),
(0.166666667, "pi/6"),
(0.833333333, "5*pi/6"),
];
let normalized = radians.rem_euclid(2.0 * PI);
let fraction = normalized / PI;
const EPSILON: f64 = 1e-9;
for &(frac, repr) in FRACTIONS {
if (fraction - frac).abs() < EPSILON {
return repr.to_string();
}
}
let neg_fraction = (radians / PI).rem_euclid(2.0);
if neg_fraction > 1.0 {
for &(frac, repr) in FRACTIONS {
if ((2.0 - neg_fraction) - frac).abs() < EPSILON {
return format!("-{}", repr);
}
}
}
format!("{:.15}", radians)
}
pub fn to_qasm2(&self) -> String {
let mut qasm = String::new();
qasm.push_str("OPENQASM 2.0;\n");
qasm.push_str("include \"qelib1.inc\";\n\n");
qasm.push_str(&format!("qreg q[{}];\n", self.system.qubit_count()));
qasm.push_str(&format!("creg c[{}];\n\n", self.system.qubit_count()));
for op in &self.recorder.operations {
match op {
Operation::Single(qubit, gate) => {
let gate_str = match gate {
Gate1::H => format!("h q[{}];", qubit),
Gate1::X => format!("x q[{}];", qubit),
Gate1::Y => format!("y q[{}];", qubit),
Gate1::Z => format!("z q[{}];", qubit),
Gate1::S => format!("s q[{}];", qubit),
Gate1::SDag => format!("sdg q[{}];", qubit),
Gate1::T => format!("t q[{}];", qubit),
Gate1::TDag => format!("tdg q[{}];", qubit),
Gate1::Rx(angle) => {
format!("rx({}) q[{}];", Self::format_angle(*angle), qubit)
}
Gate1::Ry(angle) => {
format!("ry({}) q[{}];", Self::format_angle(*angle), qubit)
}
Gate1::Rz(angle) => {
format!("rz({}) q[{}];", Self::format_angle(*angle), qubit)
}
};
qasm.push_str(&gate_str);
qasm.push('\n');
}
Operation::Two(q1, q2, gate) => {
let gate_str = match gate {
Gate2::CNOT => format!("cx q[{}],q[{}];", q1, q2),
Gate2::CY => format!("cy q[{}],q[{}];", q1, q2),
Gate2::CZ => format!("cz q[{}],q[{}];", q1, q2),
Gate2::SWAP => format!("swap q[{}],q[{}];", q1, q2),
};
qasm.push_str(&gate_str);
qasm.push('\n');
}
Operation::Measure(qubit) => {
qasm.push_str(&format!("measure q[{}] -> c[{}];\n", qubit, qubit));
}
}
}
qasm
}
}