1use std::fmt;
23use std::num::{NonZeroU32, NonZeroU64};
24use std::sync::atomic::{AtomicU64, Ordering};
25
26use crate::circuit::bit::Qubit;
27use crate::circuit::classical_expr::ClassicalExpr;
28use crate::device::Outcome;
29use crate::qis::error::QisError;
30use smallvec::SmallVec;
31
32static NEXT_CIRCUIT_ID: AtomicU64 = AtomicU64::new(1);
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
36pub struct CircuitId(NonZeroU64);
37
38impl fmt::Display for CircuitId {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 write!(f, "CircuitId({})", self.0)
42 }
43}
44
45impl CircuitId {
46 pub fn new() -> Self {
48 let id = NEXT_CIRCUIT_ID
49 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
50 current.checked_add(1).filter(|next| *next != u64::MAX)
51 })
52 .unwrap_or_else(|_| panic!("circuit identity space exhausted"));
53 Self(NonZeroU64::new(id).expect("circuit identity counter must remain non-zero"))
54 }
55}
56
57impl Default for CircuitId {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
70pub enum ClassicalType {
71 Bit,
73 Bool,
75 UInt(NonZeroU32),
77 BitVec(NonZeroU32),
79}
80
81impl ClassicalType {
82 pub fn uint(width: u32) -> Option<Self> {
86 NonZeroU32::new(width).map(Self::UInt)
87 }
88
89 pub fn bit_vec(width: u32) -> Option<Self> {
93 NonZeroU32::new(width).map(Self::BitVec)
94 }
95
96 pub fn width(self) -> u32 {
98 match self {
99 Self::Bit | Self::Bool => 1,
100 Self::UInt(width) | Self::BitVec(width) => width.get(),
101 }
102 }
103
104 pub fn zero_literal(self) -> Result<ClassicalExpr, crate::circuit::CircuitError> {
113 match self {
114 Self::Bool => Ok(ClassicalExpr::bool_literal(false)),
115 Self::Bit => Ok(ClassicalExpr::bit_literal(false)),
116 Self::UInt(width) => ClassicalExpr::uint_literal(width.get(), 0),
117 Self::BitVec(width) => ClassicalExpr::bit_vec_literal(width.get(), 0),
118 }
119 }
120
121 pub fn one_literal(self) -> Result<ClassicalExpr, crate::circuit::CircuitError> {
130 match self {
131 Self::Bool => Ok(ClassicalExpr::bool_literal(true)),
132 Self::Bit => Ok(ClassicalExpr::bit_literal(true)),
133 Self::UInt(width) => ClassicalExpr::uint_literal(width.get(), 1),
134 Self::BitVec(width) => ClassicalExpr::bit_vec_literal(width.get(), 1),
135 }
136 }
137
138 pub fn measurement_width(self) -> Option<u32> {
143 match self {
144 Self::Bit => Some(1),
145 Self::BitVec(width) => Some(width.get()),
146 Self::Bool | Self::UInt(_) => None,
147 }
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
157pub struct ClassicalVar {
158 circuit_id: CircuitId,
159 index: u32,
160 ty: ClassicalType,
161}
162
163impl ClassicalVar {
164 pub fn new(circuit_id: CircuitId, index: u32, ty: ClassicalType) -> Self {
169 Self {
170 circuit_id,
171 index,
172 ty,
173 }
174 }
175
176 pub fn id(self) -> u32 {
178 self.index
179 }
180
181 pub fn index(self) -> u32 {
183 self.index
184 }
185
186 pub fn circuit_id(self) -> CircuitId {
188 self.circuit_id
189 }
190
191 pub fn ty(self) -> ClassicalType {
193 self.ty
194 }
195
196 pub fn expr(self) -> ClassicalExpr {
198 ClassicalExpr::var(self)
199 }
200}
201
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
208pub struct ClassicalValue {
209 circuit_id: CircuitId,
210 index: u32,
211 ty: ClassicalType,
212}
213
214impl ClassicalValue {
215 pub fn new(circuit_id: CircuitId, index: u32, ty: ClassicalType) -> Self {
220 Self {
221 circuit_id,
222 index,
223 ty,
224 }
225 }
226
227 pub fn index(self) -> u32 {
229 self.index
230 }
231
232 pub fn circuit_id(self) -> CircuitId {
234 self.circuit_id
235 }
236
237 pub fn ty(self) -> ClassicalType {
239 self.ty
240 }
241
242 pub fn expr(self) -> ClassicalExpr {
244 ClassicalExpr::value(self)
245 }
246}
247
248#[derive(Debug, Clone, PartialEq, Eq)]
257pub struct Measurement {
258 value: ClassicalValue,
259 qubits: SmallVec<[Qubit; 3]>,
260}
261
262impl Measurement {
263 pub fn new(value: ClassicalValue, qubits: SmallVec<[Qubit; 3]>) -> Self {
265 Self { value, qubits }
266 }
267
268 pub fn value(&self) -> ClassicalValue {
270 self.value
271 }
272
273 pub fn expr(&self) -> ClassicalExpr {
275 self.value.expr()
276 }
277
278 pub fn qubits(&self) -> &[Qubit] {
280 &self.qubits
281 }
282
283 pub fn width(&self) -> usize {
285 self.qubits.len()
286 }
287
288 pub fn ty(&self) -> ClassicalType {
290 self.value.ty()
291 }
292
293 pub fn check_qubits(&self, num_qubits: usize) -> Result<(), QisError> {
298 for qubit in self.qubits() {
299 let index = qubit.index();
300 if index >= num_qubits {
301 return Err(QisError::IndexOutOfBounds {
302 index,
303 max: num_qubits.saturating_sub(1),
304 });
305 }
306 }
307 Ok(())
308 }
309
310 pub fn project(&self, full: &Outcome) -> Outcome {
315 Outcome::from_indices(
316 self.width(),
317 self.qubits()
318 .iter()
319 .enumerate()
320 .filter_map(|(bit, qubit)| full.is_one(qubit.index()).then_some(bit)),
321 )
322 }
323
324 pub fn project_basis(&self, basis: usize) -> Outcome {
329 Outcome::from_indices(
330 self.width(),
331 self.qubits()
332 .iter()
333 .enumerate()
334 .filter_map(|(bit, qubit)| (((basis >> qubit.index()) & 1) == 1).then_some(bit)),
335 )
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::{CircuitId, ClassicalType, ClassicalValue, ClassicalVar, Measurement};
342 use crate::circuit::Qubit;
343 use crate::device::Outcome;
344 use crate::qis::QisError;
345 use smallvec::smallvec;
346
347 #[test]
348 fn type_widths_are_reported() {
349 assert_eq!(ClassicalType::Bit.width(), 1);
350 assert_eq!(ClassicalType::Bool.width(), 1);
351 assert_eq!(ClassicalType::uint(7).unwrap().width(), 7);
352 assert_eq!(ClassicalType::bit_vec(3).unwrap().width(), 3);
353 }
354
355 #[test]
356 fn zero_width_integer_and_bit_vector_are_rejected() {
357 assert_eq!(ClassicalType::uint(0), None);
358 assert_eq!(ClassicalType::bit_vec(0), None);
359 }
360
361 #[test]
362 fn measurement_width_accepts_only_bits_and_bit_vectors() {
363 assert_eq!(ClassicalType::Bit.measurement_width(), Some(1));
364 assert_eq!(
365 ClassicalType::bit_vec(5).unwrap().measurement_width(),
366 Some(5)
367 );
368 assert_eq!(ClassicalType::Bool.measurement_width(), None);
369 assert_eq!(ClassicalType::uint(5).unwrap().measurement_width(), None);
370 }
371
372 #[test]
373 fn variables_expose_id_and_type() {
374 let var = ClassicalVar::new(CircuitId::new(), 12, ClassicalType::bit_vec(4).unwrap());
375
376 assert_eq!(var.id(), 12);
377 assert_eq!(var.ty(), ClassicalType::bit_vec(4).unwrap());
378 }
379
380 #[test]
381 fn variable_identity_includes_id_and_type() {
382 let circuit_id = CircuitId::new();
383 let bit = ClassicalVar::new(circuit_id, 1, ClassicalType::Bit);
384 let same_bit = ClassicalVar::new(circuit_id, 1, ClassicalType::Bit);
385 let bool_var = ClassicalVar::new(circuit_id, 1, ClassicalType::Bool);
386 let other_bit = ClassicalVar::new(circuit_id, 2, ClassicalType::Bit);
387
388 assert_eq!(bit, same_bit);
389 assert_ne!(bit, bool_var);
390 assert_ne!(bit, other_bit);
391 }
392
393 #[test]
394 fn measurement_exposes_value_and_qubit_order() {
395 let value = ClassicalValue::new(CircuitId::new(), 3, ClassicalType::bit_vec(2).unwrap());
396 let measurement = Measurement::new(value, smallvec![Qubit::new(1), Qubit::new(0)]);
397
398 assert_eq!(measurement.value(), value);
399 assert_eq!(measurement.ty(), ClassicalType::bit_vec(2).unwrap());
400 assert_eq!(measurement.width(), 2);
401 assert_eq!(measurement.qubits(), &[Qubit::new(1), Qubit::new(0)]);
402 assert_eq!(measurement.expr().ty(), ClassicalType::bit_vec(2).unwrap());
403 }
404
405 #[test]
406 fn measurement_projects_outcomes_in_measurement_order() {
407 let value = ClassicalValue::new(CircuitId::new(), 3, ClassicalType::bit_vec(3).unwrap());
408 let measurement = Measurement::new(
409 value,
410 smallvec![Qubit::new(2), Qubit::new(0), Qubit::new(1)],
411 );
412 let full = Outcome::from_indices(3, [0, 2]);
413
414 let projected = measurement.project(&full);
415 assert_eq!(projected.to_bitstring(3), "011");
416 assert_eq!(measurement.project_basis(0b101).to_bitstring(3), "011");
417 }
418
419 #[test]
420 fn measurement_checks_qubit_bounds() {
421 let value = ClassicalValue::new(CircuitId::new(), 3, ClassicalType::bit_vec(2).unwrap());
422 let measurement = Measurement::new(value, smallvec![Qubit::new(0), Qubit::new(2)]);
423
424 assert!(measurement.check_qubits(3).is_ok());
425 assert!(matches!(
426 measurement.check_qubits(2),
427 Err(QisError::IndexOutOfBounds { index: 2, max: 1 })
428 ));
429 }
430}