quil_rs/instruction/
qubit.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::sync::Arc;

use crate::quil::{Quil, ToQuilError};

#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, strum::EnumTryAs)]
pub enum Qubit {
    Fixed(u64),
    Placeholder(QubitPlaceholder),
    Variable(String),
}

impl Qubit {
    pub(crate) fn resolve_placeholder<R>(&mut self, resolver: R)
    where
        R: Fn(&QubitPlaceholder) -> Option<u64>,
    {
        if let Qubit::Placeholder(placeholder) = self {
            if let Some(resolved) = resolver(placeholder) {
                *self = Qubit::Fixed(resolved);
            }
        }
    }
}

impl Quil for Qubit {
    fn write(
        &self,
        writer: &mut impl std::fmt::Write,
        fall_back_to_debug: bool,
    ) -> std::result::Result<(), crate::quil::ToQuilError> {
        use Qubit::*;
        match self {
            Fixed(value) => write!(writer, "{value}").map_err(Into::into),
            Placeholder(_) => {
                if fall_back_to_debug {
                    write!(writer, "{:?}", self).map_err(Into::into)
                } else {
                    Err(ToQuilError::UnresolvedQubitPlaceholder)
                }
            }
            Variable(value) => write!(writer, "{value}").map_err(Into::into),
        }
    }
}

type QubitPlaceholderInner = Arc<()>;

/// An opaque placeholder for a qubit whose index may be assigned
/// at a later time.
#[derive(Clone, Eq)]
pub struct QubitPlaceholder(QubitPlaceholderInner);

impl QubitPlaceholder {
    fn address(&self) -> usize {
        &*self.0 as *const _ as usize
    }
}

impl Default for QubitPlaceholder {
    fn default() -> Self {
        Self(Arc::new(()))
    }
}

impl std::fmt::Debug for QubitPlaceholder {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "QubitPlaceholder({:#X})", self.address())
    }
}

impl std::hash::Hash for QubitPlaceholder {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.address().hash(state);
    }
}

impl PartialEq for QubitPlaceholder {
    #[allow(clippy::ptr_eq)]
    fn eq(&self, other: &Self) -> bool {
        self.address().eq(&other.address())
    }
}

impl PartialOrd for QubitPlaceholder {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for QubitPlaceholder {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.address().cmp(&other.address())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use regex::Regex;
    use rstest::rstest;

    #[test]
    fn resolve_placeholder() {
        let mut qubit = Qubit::Placeholder(QubitPlaceholder::default());
        qubit.resolve_placeholder(|_| Some(0));
        assert_eq!(qubit, Qubit::Fixed(0));
    }

    #[rstest]
    #[case(Qubit::Fixed(0), Ok("0"), "0")]
    #[case(
        Qubit::Variable("q".to_string()),
        Ok("q"),
        "q"
    )]
    #[case(
        Qubit::Placeholder(QubitPlaceholder::default()),
        Err(ToQuilError::UnresolvedQubitPlaceholder),
        r"Placeholder\(QubitPlaceholder\(0x[0-9,A-Z]+\)\)"
    )]
    fn quil_format(
        #[case] input: Qubit,
        #[case] expected_quil: crate::quil::ToQuilResult<&str>,
        #[case] expected_debug: &str,
    ) {
        assert_eq!(input.to_quil(), expected_quil.map(|s| s.to_string()));
        let re = Regex::new(expected_debug).unwrap();
        assert!(re.is_match(&input.to_quil_or_debug()));
    }
}