quil_rs/instruction/
qubit.rs

1use std::sync::Arc;
2
3use crate::quil::{Quil, ToQuilError};
4
5#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, strum::EnumTryAs)]
6pub enum Qubit {
7    Fixed(u64),
8    Placeholder(QubitPlaceholder),
9    Variable(String),
10}
11
12impl Qubit {
13    pub(crate) fn resolve_placeholder<R>(&mut self, resolver: R)
14    where
15        R: Fn(&QubitPlaceholder) -> Option<u64>,
16    {
17        if let Qubit::Placeholder(placeholder) = self {
18            if let Some(resolved) = resolver(placeholder) {
19                *self = Qubit::Fixed(resolved);
20            }
21        }
22    }
23}
24
25impl Quil for Qubit {
26    fn write(
27        &self,
28        writer: &mut impl std::fmt::Write,
29        fall_back_to_debug: bool,
30    ) -> std::result::Result<(), crate::quil::ToQuilError> {
31        use Qubit::*;
32        match self {
33            Fixed(value) => write!(writer, "{value}").map_err(Into::into),
34            Placeholder(_) => {
35                if fall_back_to_debug {
36                    write!(writer, "{:?}", self).map_err(Into::into)
37                } else {
38                    Err(ToQuilError::UnresolvedQubitPlaceholder)
39                }
40            }
41            Variable(value) => write!(writer, "{value}").map_err(Into::into),
42        }
43    }
44}
45
46type QubitPlaceholderInner = Arc<()>;
47
48/// An opaque placeholder for a qubit whose index may be assigned
49/// at a later time.
50#[derive(Clone, Eq)]
51pub struct QubitPlaceholder(QubitPlaceholderInner);
52
53impl QubitPlaceholder {
54    fn address(&self) -> usize {
55        &*self.0 as *const _ as usize
56    }
57}
58
59impl Default for QubitPlaceholder {
60    fn default() -> Self {
61        Self(Arc::new(()))
62    }
63}
64
65impl std::fmt::Debug for QubitPlaceholder {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        write!(f, "QubitPlaceholder({:#X})", self.address())
68    }
69}
70
71impl std::hash::Hash for QubitPlaceholder {
72    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
73        self.address().hash(state);
74    }
75}
76
77impl PartialEq for QubitPlaceholder {
78    #[allow(clippy::ptr_eq)]
79    fn eq(&self, other: &Self) -> bool {
80        self.address().eq(&other.address())
81    }
82}
83
84impl PartialOrd for QubitPlaceholder {
85    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
86        Some(self.cmp(other))
87    }
88}
89
90impl Ord for QubitPlaceholder {
91    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
92        self.address().cmp(&other.address())
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    use regex::Regex;
100    use rstest::rstest;
101
102    #[test]
103    fn resolve_placeholder() {
104        let mut qubit = Qubit::Placeholder(QubitPlaceholder::default());
105        qubit.resolve_placeholder(|_| Some(0));
106        assert_eq!(qubit, Qubit::Fixed(0));
107    }
108
109    #[rstest]
110    #[case(Qubit::Fixed(0), Ok("0"), "0")]
111    #[case(
112        Qubit::Variable("q".to_string()),
113        Ok("q"),
114        "q"
115    )]
116    #[case(
117        Qubit::Placeholder(QubitPlaceholder::default()),
118        Err(ToQuilError::UnresolvedQubitPlaceholder),
119        r"Placeholder\(QubitPlaceholder\(0x[0-9,A-Z]+\)\)"
120    )]
121    fn quil_format(
122        #[case] input: Qubit,
123        #[case] expected_quil: crate::quil::ToQuilResult<&str>,
124        #[case] expected_debug: &str,
125    ) {
126        assert_eq!(input.to_quil(), expected_quil.map(|s| s.to_string()));
127        let re = Regex::new(expected_debug).unwrap();
128        assert!(re.is_match(&input.to_quil_or_debug()));
129    }
130}