Skip to main content

cqlib_core/circuit/
bit.rs

1// This code is part of Cqlib.
2//
3// (C) Copyright China Telecom Quantum Group 2025
4//
5// This code is licensed under the Apache License, Version 2.0. You may
6// obtain a copy of this license in the LICENSE.txt file in the root directory
7// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8//
9// Any modifications or derivative works of this code must retain this
10// copyright notice, and modified files need to carry a notice indicating
11// that they have been altered from the originals.
12
13//! # Qubit Module
14//!
15//! This module defines the fundamental addressing units for quantum circuits.
16//!
17//! ## Architecture Design
18//!
19//! This library adopts a **Handle Pattern** (also known as Entity-Component style) for qubit management:
20//!
21//! - **[`Qubit`]** acts as a lightweight, `Copy`-able logical identifier. It does not hold state
22//!   or topological information itself.
23//! - State and connectivity are managed by the parent `Circuit` or `DAG`.
24//!
25//! This design ensures strictly $O(1)$ cloning costs and cache-friendly memory layouts during
26//! complex compilation passes.
27
28use std::{fmt, hash::Hash};
29use thiserror::Error;
30
31/// Errors returned when converting an integer into a [`Qubit`].
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
33pub enum QubitError {
34    /// The supplied signed integer was negative.
35    #[error("qubit index must be non-negative, got {0}")]
36    NegativeIndex(i128),
37    /// The supplied integer cannot be represented by the internal `u32` ID.
38    #[error("qubit index {0} exceeds u32::MAX")]
39    IndexTooLarge(u128),
40}
41
42/// A lightweight logical qubit identifier.
43///
44/// Equality compares only the numeric identifier. A `Qubit` does not carry a circuit
45/// identity, physical-device location, or state-vector position. The owning circuit and
46/// the matrix or simulator qubit order determine those meanings.
47///
48/// It wraps a `u32` to provide a compact representation. Numeric IDs may be sparse,
49/// so callers must not assume that [`Qubit::index`] is a valid position in a circuit's
50/// dense qubit storage or a simulator state vector.
51///
52/// # Examples
53///
54/// Basic usage:
55///
56/// ```rust
57/// use cqlib_core::circuit::bit::Qubit;
58///
59/// // Create a logical qubit with numeric identifier 5.
60/// let q = Qubit::new(5);
61///
62/// assert_eq!(q.id(), 5);
63/// assert_eq!(q.index(), 5usize);
64/// ```
65#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
66#[repr(transparent)]
67pub struct Qubit(u32);
68
69impl fmt::Display for Qubit {
70    /// Formats the qubit identifier.
71    ///
72    /// The output format is `Q<id>` (e.g., `Q0`, `Q12`).
73    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74        write!(f, "Q{}", self.0)
75    }
76}
77
78impl Qubit {
79    /// Creates a logical qubit with the supplied numeric identifier.
80    ///
81    /// This is the only way for external users to create a Qubit instance,
82    /// as the internal field is private to enforce encapsulation.
83    #[inline]
84    pub const fn new(id: u32) -> Self {
85        Self(id)
86    }
87
88    /// Returns the raw underlying identifier as a `u32`.
89    ///
90    /// Use this when you need the compact 4-byte representation.
91    #[inline]
92    pub const fn id(&self) -> u32 {
93        self.0
94    }
95
96    /// Returns the numeric identifier as a `usize`.
97    ///
98    /// This conversion does not map the qubit to its position in a particular
99    /// circuit, matrix, or simulator. Such positions depend on the ordering
100    /// maintained by the owning data structure.
101    #[inline]
102    pub const fn index(&self) -> usize {
103        self.0 as usize
104    }
105}
106
107macro_rules! impl_qubit_from_unsigned {
108    ($($t:ty),*) => {
109        $(
110            impl From<$t> for Qubit {
111                fn from(idx: $t) -> Self {
112                    Self(idx as u32)
113                }
114            }
115        )*
116    };
117}
118macro_rules! impl_qubit_try_from_signed {
119    ($($t:ty),*) => {
120        $(
121            impl TryFrom<$t> for Qubit {
122                type Error = QubitError;
123
124                fn try_from(idx: $t) -> Result<Self, Self::Error> {
125                    if idx < 0 {
126                        Err(QubitError::NegativeIndex(idx as i128))
127                    } else if idx as u128 > u32::MAX as u128 {
128                        Err(QubitError::IndexTooLarge(idx as u128))
129                    } else {
130                        Ok(Self(idx as u32))
131                    }
132                }
133            }
134        )*
135    };
136}
137
138// Implement From for types that cannot overflow u32
139impl_qubit_from_unsigned!(u32, u16, u8);
140impl_qubit_try_from_signed!(i8, i16, i32, isize);
141
142macro_rules! impl_qubit_try_from_unsigned {
143    ($($t:ty),*) => {
144        $(
145            impl TryFrom<$t> for Qubit {
146                type Error = QubitError;
147
148                fn try_from(idx: $t) -> Result<Self, Self::Error> {
149                    if idx as u128 > u32::MAX as u128 {
150                        Err(QubitError::IndexTooLarge(idx as u128))
151                    } else {
152                        Ok(Self(idx as u32))
153                    }
154                }
155            }
156        )*
157    };
158}
159
160impl_qubit_try_from_unsigned!(usize);
161
162#[test]
163fn test_qubit_creation_and_display() {
164    let q0 = Qubit(0);
165    let q1 = Qubit(1);
166
167    assert_eq!(q0.id(), 0);
168    assert_eq!(q1.id(), 1);
169    assert_ne!(q0, q1);
170
171    assert_eq!(format!("{}", q0), "Q0");
172    assert_eq!(format!("{}", q1), "Q1");
173
174    let q0_1 = Qubit(0);
175    assert_eq!(q0, q0_1);
176}
177
178#[test]
179fn test_qubit_try_from_usize() {
180    // Valid indices should work
181    assert!(Qubit::try_from(0usize).is_ok());
182    assert!(Qubit::try_from(100usize).is_ok());
183    if usize::BITS >= 32 {
184        assert!(Qubit::try_from(u32::MAX as usize).is_ok());
185    }
186
187    // Overflow on 64-bit systems
188    if usize::BITS > 32 {
189        let overflow_idx = (u32::MAX as usize) + 1;
190        let result = Qubit::try_from(overflow_idx);
191        assert!(result.is_err());
192        assert_eq!(
193            result.unwrap_err(),
194            QubitError::IndexTooLarge(overflow_idx as u128)
195        );
196    }
197}
198
199#[test]
200fn test_qubit_try_from_isize() {
201    // Valid indices should work
202    assert!(Qubit::try_from(0isize).is_ok());
203    assert!(Qubit::try_from(100isize).is_ok());
204    if isize::BITS > 32 {
205        assert!(Qubit::try_from(u32::MAX as isize).is_ok());
206    } else {
207        assert!(Qubit::try_from(isize::MAX).is_ok());
208    }
209
210    // Negative index should fail
211    let result = Qubit::try_from(-1isize);
212    assert!(result.is_err());
213    assert_eq!(result.unwrap_err(), QubitError::NegativeIndex(-1));
214
215    // Overflow on 64-bit systems
216    if isize::BITS > 32 {
217        let overflow_idx = (u32::MAX as isize) + 1;
218        let result = Qubit::try_from(overflow_idx);
219        assert!(result.is_err());
220        assert_eq!(
221            result.unwrap_err(),
222            QubitError::IndexTooLarge(overflow_idx as u128)
223        );
224    }
225}
226
227#[test]
228fn test_qubit_from_small_types() {
229    // These should all work via From (infallible)
230    let _: Qubit = 0u8.into();
231    let _: Qubit = 100u8.into();
232    let _: Qubit = 0u16.into();
233    let _: Qubit = 1000u16.into();
234    let _: Qubit = 0u32.into();
235    let _: Qubit = 100000u32.into();
236
237    assert_eq!(Qubit::try_from(0i8), Ok(Qubit::new(0)));
238    assert_eq!(Qubit::try_from(100i8), Ok(Qubit::new(100)));
239    assert_eq!(Qubit::try_from(1000i16), Ok(Qubit::new(1000)));
240    assert_eq!(Qubit::try_from(100000i32), Ok(Qubit::new(100000)));
241    assert_eq!(Qubit::try_from(-1i8), Err(QubitError::NegativeIndex(-1)));
242    assert_eq!(Qubit::try_from(-1i16), Err(QubitError::NegativeIndex(-1)));
243    assert_eq!(Qubit::try_from(-1i32), Err(QubitError::NegativeIndex(-1)));
244}