use crate::circuit::Circuit;
use crate::operator::Operator;
#[derive(Debug)]
pub struct Qubit {
index: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ClassicalBit {
qubit_index: usize,
}
impl ClassicalBit {
#[inline]
#[must_use]
pub fn qubit(&self) -> usize {
self.qubit_index
}
}
pub struct QuantumBuilder {
num_qubits: usize,
circuit: Circuit,
}
impl QuantumBuilder {
#[must_use]
pub fn new<const N: usize>() -> (Self, [Qubit; N]) {
let builder = Self {
num_qubits: N,
circuit: Circuit::new(N),
};
let qubits = std::array::from_fn(|i| Qubit { index: i });
(builder, qubits)
}
pub fn h(&mut self, q: Qubit) -> Qubit {
self.circuit
.hadamard(q.index)
.expect("qubit index valid by construction");
q
}
pub fn x(&mut self, q: Qubit) -> Qubit {
self.circuit
.pauli_x(q.index)
.expect("qubit index valid by construction");
q
}
pub fn y(&mut self, q: Qubit) -> Qubit {
self.circuit
.pauli_y(q.index)
.expect("qubit index valid by construction");
q
}
pub fn z(&mut self, q: Qubit) -> Qubit {
self.circuit
.pauli_z(q.index)
.expect("qubit index valid by construction");
q
}
pub fn s(&mut self, q: Qubit) -> Qubit {
self.circuit
.phase_s(q.index)
.expect("qubit index valid by construction");
q
}
pub fn t(&mut self, q: Qubit) -> Qubit {
self.circuit
.phase_t(q.index)
.expect("qubit index valid by construction");
q
}
pub fn rx(&mut self, q: Qubit, theta: f64) -> Qubit {
self.circuit
.rx(q.index, theta)
.expect("qubit index valid by construction");
q
}
pub fn ry(&mut self, q: Qubit, theta: f64) -> Qubit {
self.circuit
.ry(q.index, theta)
.expect("qubit index valid by construction");
q
}
pub fn rz(&mut self, q: Qubit, theta: f64) -> Qubit {
self.circuit
.rz(q.index, theta)
.expect("qubit index valid by construction");
q
}
pub fn cx(&mut self, control: Qubit, target: Qubit) -> (Qubit, Qubit) {
self.circuit
.cnot(control.index, target.index)
.expect("qubit index valid by construction");
(control, target)
}
pub fn cz(&mut self, a: Qubit, b: Qubit) -> (Qubit, Qubit) {
self.circuit
.cz(a.index, b.index)
.expect("qubit index valid by construction");
(a, b)
}
pub fn swap(&mut self, a: Qubit, b: Qubit) -> (Qubit, Qubit) {
self.circuit
.swap(a.index, b.index)
.expect("qubit index valid by construction");
(b, a)
}
pub fn ccx(&mut self, c0: Qubit, c1: Qubit, target: Qubit) -> (Qubit, Qubit, Qubit) {
self.circuit
.toffoli(c0.index, c1.index, target.index)
.expect("qubit index valid by construction");
(c0, c1, target)
}
pub fn cswap(&mut self, control: Qubit, a: Qubit, b: Qubit) -> (Qubit, Qubit, Qubit) {
self.circuit
.fredkin(control.index, a.index, b.index)
.expect("qubit index valid by construction");
(control, b, a) }
pub fn cu(&mut self, control: Qubit, target: Qubit, u: &Operator) -> (Qubit, Qubit) {
self.circuit
.controlled_u(control.index, target.index, u)
.expect("qubit index valid by construction");
(control, target)
}
pub fn measure(&mut self, q: Qubit) -> (ClassicalBit, Qubit) {
self.circuit
.measure(q.index)
.expect("qubit index valid by construction");
(
ClassicalBit {
qubit_index: q.index,
},
q,
)
}
#[must_use]
pub fn build<const N: usize>(self, _qubits: [Qubit; N]) -> Circuit {
assert_eq!(
N, self.num_qubits,
"must return all {0} qubits, got {N}",
self.num_qubits
);
self.circuit
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::NORM_TOLERANCE;
#[test]
fn test_bell_state() {
let (mut b, [q0, q1]) = QuantumBuilder::new::<2>();
let q0 = b.h(q0);
let (q0, q1) = b.cx(q0, q1);
let circuit = b.build([q0, q1]);
let result = circuit.execute().unwrap();
assert!((result.probability(0).unwrap() - 0.5).abs() < NORM_TOLERANCE);
assert!((result.probability(3).unwrap() - 0.5).abs() < NORM_TOLERANCE);
}
#[test]
fn test_ghz_state() {
let (mut b, [q0, q1, q2]) = QuantumBuilder::new::<3>();
let q0 = b.h(q0);
let (q0, q1) = b.cx(q0, q1);
let (q0, q2) = b.cx(q0, q2);
let circuit = b.build([q0, q1, q2]);
let result = circuit.execute().unwrap();
assert!((result.probability(0).unwrap() - 0.5).abs() < NORM_TOLERANCE);
assert!((result.probability(7).unwrap() - 0.5).abs() < NORM_TOLERANCE);
}
#[test]
fn test_measurement() {
let (mut b, [q0]) = QuantumBuilder::new::<1>();
let q0 = b.h(q0);
let (bit, q0) = b.measure(q0);
assert_eq!(bit.qubit(), 0);
let circuit = b.build([q0]);
assert_eq!(circuit.num_gates(), 2); }
#[test]
fn test_swap_returns_swapped() {
let (mut b, [q0, q1]) = QuantumBuilder::new::<2>();
let q0 = b.x(q0); let (q0, q1) = b.swap(q0, q1);
let circuit = b.build([q0, q1]);
let result = circuit.execute().unwrap();
assert!((result.probability(1).unwrap() - 1.0).abs() < NORM_TOLERANCE);
}
#[test]
fn test_rotation_gates() {
let (mut b, [q0]) = QuantumBuilder::new::<1>();
let q0 = b.rx(q0, std::f64::consts::PI);
let circuit = b.build([q0]);
let result = circuit.execute().unwrap();
assert!((result.probability(1).unwrap() - 1.0).abs() < NORM_TOLERANCE);
}
#[test]
fn test_toffoli() {
let (mut b, [q0, q1, q2]) = QuantumBuilder::new::<3>();
let q0 = b.x(q0);
let q1 = b.x(q1);
let (q0, q1, q2) = b.ccx(q0, q1, q2);
let circuit = b.build([q0, q1, q2]);
let result = circuit.execute().unwrap();
assert!((result.probability(7).unwrap() - 1.0).abs() < NORM_TOLERANCE);
}
#[test]
fn test_controlled_u() {
let (mut b, [q0, q1]) = QuantumBuilder::new::<2>();
let q0 = b.x(q0); let (q0, q1) = b.cu(q0, q1, &Operator::hadamard());
let circuit = b.build([q0, q1]);
let result = circuit.execute().unwrap();
assert!((result.probability(2).unwrap() - 0.5).abs() < NORM_TOLERANCE);
assert!((result.probability(3).unwrap() - 0.5).abs() < NORM_TOLERANCE);
}
}