use crate::error::{Error, Result};
use crate::gate::{Gate1, Gate2};
use crate::op::Op;
use crate::qubit::{ClassicalBit, QubitId};
#[derive(Clone, PartialEq, Debug)]
pub struct Circuit {
ops: Vec<Op>,
num_qubits: usize,
num_classical: usize,
}
impl Circuit {
#[must_use]
pub fn new(num_qubits: usize) -> Self {
Self {
ops: Vec::new(),
num_qubits,
num_classical: 0,
}
}
#[must_use]
pub fn with_classical(num_qubits: usize, num_classical: usize) -> Self {
Self {
ops: Vec::new(),
num_qubits,
num_classical,
}
}
#[inline]
#[must_use]
pub fn num_qubits(&self) -> usize {
self.num_qubits
}
#[inline]
#[must_use]
pub fn num_classical(&self) -> usize {
self.num_classical
}
#[inline]
#[must_use]
pub fn ops(&self) -> &[Op] {
&self.ops
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.ops.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.ops.is_empty()
}
pub fn gate1(&mut self, gate: Gate1, target: usize) -> &mut Self {
self.ops.push(Op::Apply1 {
gate,
target: QubitId(target),
});
self
}
pub fn gate2(&mut self, gate: Gate2, a: usize, b: usize) -> &mut Self {
self.ops.push(Op::Apply2 {
gate,
a: QubitId(a),
b: QubitId(b),
});
self
}
pub fn controlled(&mut self, controls: &[usize], gate: Gate1, target: usize) -> &mut Self {
self.ops.push(Op::Controlled {
controls: controls.iter().map(|&c| QubitId(c)).collect(),
gate,
target: QubitId(target),
});
self
}
pub fn compose(&mut self, sub: &Circuit) -> &mut Self {
self.ops.extend_from_slice(&sub.ops);
self
}
pub fn h(&mut self, q: usize) -> &mut Self {
self.gate1(Gate1::h(), q)
}
pub fn x(&mut self, q: usize) -> &mut Self {
self.gate1(Gate1::x(), q)
}
pub fn y(&mut self, q: usize) -> &mut Self {
self.gate1(Gate1::y(), q)
}
pub fn z(&mut self, q: usize) -> &mut Self {
self.gate1(Gate1::z(), q)
}
pub fn s(&mut self, q: usize) -> &mut Self {
self.gate1(Gate1::s(), q)
}
pub fn t(&mut self, q: usize) -> &mut Self {
self.gate1(Gate1::t(), q)
}
pub fn rx(&mut self, q: usize, theta: f64) -> &mut Self {
self.gate1(Gate1::rx(theta), q)
}
pub fn ry(&mut self, q: usize, theta: f64) -> &mut Self {
self.gate1(Gate1::ry(theta), q)
}
pub fn rz(&mut self, q: usize, theta: f64) -> &mut Self {
self.gate1(Gate1::rz(theta), q)
}
pub fn phase(&mut self, q: usize, lambda: f64) -> &mut Self {
self.gate1(Gate1::phase(lambda), q)
}
pub fn cnot(&mut self, control: usize, target: usize) -> &mut Self {
self.gate2(Gate2::cnot(), control, target)
}
pub fn cz(&mut self, a: usize, b: usize) -> &mut Self {
self.gate2(Gate2::cz(), a, b)
}
pub fn swap(&mut self, a: usize, b: usize) -> &mut Self {
self.gate2(Gate2::swap(), a, b)
}
pub fn measure(&mut self, qubit: usize, into: usize) -> &mut Self {
self.ops.push(Op::Measure {
qubit: QubitId(qubit),
into: ClassicalBit(into),
});
self
}
pub fn x_if(&mut self, bit: usize, q: usize) -> &mut Self {
self.if_classic(
bit,
Op::Apply1 {
gate: Gate1::x(),
target: QubitId(q),
},
)
}
pub fn z_if(&mut self, bit: usize, q: usize) -> &mut Self {
self.if_classic(
bit,
Op::Apply1 {
gate: Gate1::z(),
target: QubitId(q),
},
)
}
pub fn if_classic(&mut self, bit: usize, op: Op) -> &mut Self {
self.ops.push(Op::IfClassic {
bit: ClassicalBit(bit),
then: Box::new(op),
});
self
}
pub(crate) fn push_op(&mut self, op: Op) {
self.ops.push(op);
}
pub fn validate(&self) -> Result<()> {
for op in &self.ops {
self.validate_op(op)?;
}
Ok(())
}
fn validate_op(&self, op: &Op) -> Result<()> {
match op {
Op::Apply1 { target, .. } => self.check_qubit(*target),
Op::Apply2 { a, b, .. } => {
self.check_qubit(*a)?;
self.check_qubit(*b)?;
if a == b {
return Err(Error::DuplicateQubit { qubit: *a });
}
Ok(())
}
Op::Controlled {
controls, target, ..
} => {
self.check_qubit(*target)?;
for c in controls {
self.check_qubit(*c)?;
if c == target {
return Err(Error::DuplicateQubit { qubit: *c });
}
}
Ok(())
}
Op::Measure { qubit, into } => {
self.check_qubit(*qubit)?;
self.check_classical(*into)
}
Op::IfClassic { bit, then } => {
self.check_classical(*bit)?;
self.validate_op(then)
}
}
}
fn check_qubit(&self, q: QubitId) -> Result<()> {
if q.index() < self.num_qubits {
Ok(())
} else {
Err(Error::QubitOutOfRange {
qubit: q,
num_qubits: self.num_qubits,
})
}
}
fn check_classical(&self, b: ClassicalBit) -> Result<()> {
if b.index() < self.num_classical {
Ok(())
} else {
Err(Error::ClassicalBitOutOfRange {
bit: b,
num_classical: self.num_classical,
})
}
}
pub fn to_qasm(&self) -> Result<String> {
crate::qasm::emit(self)
}
pub fn from_qasm(src: &str) -> Result<Self> {
crate::qasm::parse(src)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fluent_chaining_records_ops() {
let mut c = Circuit::new(2);
c.h(0).cnot(0, 1);
assert_eq!(c.len(), 2);
assert!(c.validate().is_ok());
}
#[test]
fn out_of_range_qubit_is_rejected() {
let mut c = Circuit::new(2);
c.h(5);
assert!(matches!(
c.validate(),
Err(Error::QubitOutOfRange { num_qubits: 2, .. })
));
}
#[test]
fn duplicate_qubit_in_two_qubit_gate_is_rejected() {
let mut c = Circuit::new(2);
c.cnot(1, 1);
assert!(matches!(c.validate(), Err(Error::DuplicateQubit { .. })));
}
#[test]
fn measure_into_missing_classical_bit_is_rejected() {
let mut c = Circuit::new(1); c.measure(0, 0);
assert!(matches!(
c.validate(),
Err(Error::ClassicalBitOutOfRange { .. })
));
}
#[test]
fn compose_inlines_ops() {
let mut sub = Circuit::new(2);
sub.h(0).cnot(0, 1);
let mut c = Circuit::new(2);
c.compose(&sub).x(0);
assert_eq!(c.len(), 3);
}
#[test]
fn classical_control_validates_recursively() {
let mut c = Circuit::with_classical(1, 1);
c.x_if(0, 3); assert!(matches!(c.validate(), Err(Error::QubitOutOfRange { .. })));
}
}