Skip to main content

quil_rs/instruction/
classical.rs

1#[cfg(feature = "stubs")]
2use pyo3_stub_gen::derive::{
3    gen_stub_pyclass, gen_stub_pyclass_complex_enum, gen_stub_pyclass_enum,
4};
5
6use super::MemoryReference;
7
8use crate::{floating_point_eq, pickleable_new, quil::Quil};
9
10#[derive(Clone, Debug, Hash, PartialEq)]
11#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
12#[cfg_attr(
13    feature = "python",
14    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
15)]
16pub struct Arithmetic {
17    pub operator: ArithmeticOperator,
18    pub destination: MemoryReference,
19    pub source: ArithmeticOperand,
20}
21
22pickleable_new! {
23    impl Arithmetic {
24        pub fn new(
25            operator: ArithmeticOperator,
26            destination: MemoryReference,
27            source: ArithmeticOperand,
28        );
29    }
30}
31
32impl Quil for Arithmetic {
33    fn write(
34        &self,
35        f: &mut impl std::fmt::Write,
36        fall_back_to_debug: bool,
37    ) -> crate::quil::ToQuilResult<()> {
38        self.operator.write(f, fall_back_to_debug)?;
39        write!(f, " ")?;
40        self.destination.write(f, fall_back_to_debug)?;
41        write!(f, " ")?;
42        self.source.write(f, fall_back_to_debug)
43    }
44}
45
46#[derive(Clone, Debug)]
47#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
48#[cfg_attr(
49    feature = "python",
50    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all)
51)]
52pub enum ArithmeticOperand {
53    LiteralInteger(i64),
54    LiteralReal(f64),
55    MemoryReference(MemoryReference),
56}
57
58impl PartialEq for ArithmeticOperand {
59    fn eq(&self, other: &Self) -> bool {
60        match (self, other) {
61            (Self::LiteralInteger(this), Self::LiteralInteger(that)) => this == that,
62            (Self::LiteralReal(this), Self::LiteralReal(that)) => {
63                floating_point_eq::f64::eq(*this, *that)
64            }
65            (Self::MemoryReference(this), Self::MemoryReference(that)) => this == that,
66            // This explicit or-pattern ensures that we'll get a compilation error if
67            // `ArithmeticOperand` grows another constructor.
68            (Self::LiteralInteger(_) | Self::LiteralReal(_) | Self::MemoryReference(_), _) => false,
69        }
70    }
71}
72
73impl Eq for ArithmeticOperand {}
74
75impl std::hash::Hash for ArithmeticOperand {
76    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
77        match self {
78            Self::LiteralInteger(operand) => operand.hash(state),
79            Self::LiteralReal(operand) => floating_point_eq::f64::hash(*operand, state),
80            Self::MemoryReference(operand) => operand.hash(state),
81        }
82    }
83}
84
85impl Quil for ArithmeticOperand {
86    fn write(
87        &self,
88        f: &mut impl std::fmt::Write,
89        fall_back_to_debug: bool,
90    ) -> crate::quil::ToQuilResult<()> {
91        match &self {
92            ArithmeticOperand::LiteralInteger(value) => write!(f, "{value}").map_err(Into::into),
93            ArithmeticOperand::LiteralReal(value) => write!(f, "{value}").map_err(Into::into),
94            ArithmeticOperand::MemoryReference(value) => value.write(f, fall_back_to_debug),
95        }
96    }
97}
98
99impl From<MemoryReference> for ArithmeticOperand {
100    fn from(memory_reference: MemoryReference) -> Self {
101        ArithmeticOperand::MemoryReference(memory_reference)
102    }
103}
104
105#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
106#[cfg_attr(feature = "stubs", gen_stub_pyclass_enum)]
107#[cfg_attr(
108    feature = "python",
109    pyo3::pyclass(
110        module = "quil.instructions",
111        eq,
112        frozen,
113        hash,
114        rename_all = "SCREAMING_SNAKE_CASE"
115    )
116)]
117pub enum ArithmeticOperator {
118    Add,
119    Subtract,
120    Divide,
121    Multiply,
122}
123
124impl Quil for ArithmeticOperator {
125    fn write(
126        &self,
127        f: &mut impl std::fmt::Write,
128        _fall_back_to_debug: bool,
129    ) -> crate::quil::ToQuilResult<()> {
130        match &self {
131            ArithmeticOperator::Add => write!(f, "ADD"),
132            ArithmeticOperator::Subtract => write!(f, "SUB"),
133            ArithmeticOperator::Divide => write!(f, "DIV"),
134            ArithmeticOperator::Multiply => write!(f, "MUL"),
135        }
136        .map_err(Into::into)
137    }
138}
139
140#[derive(Clone, Debug, Hash, PartialEq, Eq)]
141#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
142#[cfg_attr(
143    feature = "python",
144    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all)
145)]
146pub enum BinaryOperand {
147    LiteralInteger(i64),
148    MemoryReference(MemoryReference),
149}
150
151impl Quil for BinaryOperand {
152    fn write(
153        &self,
154        f: &mut impl std::fmt::Write,
155        fall_back_to_debug: bool,
156    ) -> crate::quil::ToQuilResult<()> {
157        match &self {
158            BinaryOperand::LiteralInteger(value) => write!(f, "{value}").map_err(Into::into),
159            BinaryOperand::MemoryReference(value) => value.write(f, fall_back_to_debug),
160        }
161    }
162}
163
164/// Bitwise operators.  These are "binary" in the sense that they operate on *binary
165/// representations*, not in the sense that they take two arguments (although they also do that).
166#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
167#[cfg_attr(feature = "stubs", gen_stub_pyclass_enum)]
168#[cfg_attr(
169    feature = "python",
170    pyo3::pyclass(
171        module = "quil.instructions",
172        eq,
173        frozen,
174        hash,
175        rename_all = "SCREAMING_SNAKE_CASE"
176    )
177)]
178pub enum BinaryOperator {
179    /// Bitwise and.  The same as Rust's and Python's `&`.
180    And,
181
182    /// Bitwise (inclusive) or.  The same as Rust's and Python's `|`.
183    Ior,
184
185    /// Bitwise exclusive or.  The same as Rust's and Python's `^`.
186    Xor,
187
188    /// Bitwise left shift.  The same as Rust's and Python's `<<`.
189    ///
190    /// As per the Quil specification, shifting by a negative number of bits is forbidden, and
191    /// shifting by as many or more bits than are present in a word fills the entire word with `0`s.
192    Shl,
193
194    /// Bitwise logical right shift.  The same as Rust's unsigned-type `>>`.  Python does not
195    /// provide this operation, as its integers are natively infinite-width.
196    ///
197    /// As per the Quil specification, shifting by a negative number of bits is forbidden, and
198    /// shifting by as many or more bits than are present in a word fills the entire word with `0`s.
199    Shr,
200
201    /// Bitwise logical right shift.  The same as Rust's signed-type `>>` and the same as Python's
202    /// `>>` (as Python natively has infinite-width signed integers).  `ASHR n k` is equivalent to
203    /// `⌊n / 2ᵏ⌋` assuming a 2's complement representation of integers.
204    ///
205    /// As per the Quil specification, shifting by a negative number of bits is forbidden, and
206    /// shifting by as many or more bits than are present in a word fills the entire word with the
207    /// existing top bit of the word.
208    Ashr,
209}
210
211impl Quil for BinaryOperator {
212    fn write(
213        &self,
214        f: &mut impl std::fmt::Write,
215        _fall_back_to_debug: bool,
216    ) -> crate::quil::ToQuilResult<()> {
217        match self {
218            BinaryOperator::And => write!(f, "AND"),
219            BinaryOperator::Ior => write!(f, "IOR"),
220            BinaryOperator::Xor => write!(f, "XOR"),
221            BinaryOperator::Shl => write!(f, "SHL"),
222            BinaryOperator::Shr => write!(f, "SHR"),
223            BinaryOperator::Ashr => write!(f, "ASHR"),
224        }
225        .map_err(Into::into)
226    }
227}
228
229#[derive(Clone, Debug, Hash, PartialEq, Eq)]
230#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
231#[cfg_attr(
232    feature = "python",
233    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
234)]
235pub struct BinaryLogic {
236    pub operator: BinaryOperator,
237    pub destination: MemoryReference,
238    pub source: BinaryOperand,
239}
240
241impl Quil for BinaryLogic {
242    fn write(
243        &self,
244        f: &mut impl std::fmt::Write,
245        fall_back_to_debug: bool,
246    ) -> crate::quil::ToQuilResult<()> {
247        self.operator.write(f, fall_back_to_debug)?;
248        write!(f, " ")?;
249        self.destination.write(f, fall_back_to_debug)?;
250        write!(f, " ")?;
251        self.source.write(f, fall_back_to_debug)?;
252        Ok(())
253    }
254}
255
256pickleable_new! {
257    impl BinaryLogic {
258        pub fn new(
259            operator: BinaryOperator,
260            destination: MemoryReference,
261            source: BinaryOperand,
262        );
263    }
264}
265
266#[derive(Clone, Debug, Hash, PartialEq, Eq)]
267#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
268#[cfg_attr(
269    feature = "python",
270    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
271)]
272pub struct Convert {
273    pub destination: MemoryReference,
274    pub source: MemoryReference,
275}
276
277pickleable_new! {
278    impl Convert {
279        pub fn new(destination: MemoryReference, source: MemoryReference);
280    }
281}
282
283impl Quil for Convert {
284    fn write(
285        &self,
286        f: &mut impl std::fmt::Write,
287        fall_back_to_debug: bool,
288    ) -> crate::quil::ToQuilResult<()> {
289        write!(f, "CONVERT ")?;
290        self.destination.write(f, fall_back_to_debug)?;
291        write!(f, " ")?;
292        self.source.write(f, fall_back_to_debug)?;
293        Ok(())
294    }
295}
296
297#[derive(Clone, Debug, Hash, PartialEq)]
298#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
299#[cfg_attr(
300    feature = "python",
301    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
302)]
303pub struct Move {
304    pub destination: MemoryReference,
305    pub source: ArithmeticOperand,
306}
307
308pickleable_new! {
309    impl Move {
310        pub fn new(destination: MemoryReference, source: ArithmeticOperand);
311    }
312}
313
314impl Quil for Move {
315    fn write(
316        &self,
317        f: &mut impl std::fmt::Write,
318        fall_back_to_debug: bool,
319    ) -> crate::quil::ToQuilResult<()> {
320        write!(f, "MOVE ")?;
321        self.destination.write(f, fall_back_to_debug)?;
322        write!(f, " ")?;
323        self.source.write(f, fall_back_to_debug)?;
324        Ok(())
325    }
326}
327
328#[derive(Clone, Debug, Hash, PartialEq)]
329#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
330#[cfg_attr(
331    feature = "python",
332    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
333)]
334pub struct Exchange {
335    pub left: MemoryReference,
336    pub right: MemoryReference,
337}
338
339impl Quil for Exchange {
340    fn write(
341        &self,
342        f: &mut impl std::fmt::Write,
343        fall_back_to_debug: bool,
344    ) -> crate::quil::ToQuilResult<()> {
345        write!(f, "EXCHANGE ")?;
346        self.left.write(f, fall_back_to_debug)?;
347        write!(f, " ")?;
348        self.right.write(f, fall_back_to_debug)?;
349        Ok(())
350    }
351}
352
353pickleable_new! {
354    impl Exchange {
355        pub fn new(left: MemoryReference, right: MemoryReference);
356    }
357}
358
359#[derive(Clone, Debug, Hash, PartialEq)]
360#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
361#[cfg_attr(
362    feature = "python",
363    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
364)]
365pub struct Comparison {
366    pub operator: ComparisonOperator,
367    pub destination: MemoryReference,
368    pub lhs: MemoryReference,
369    pub rhs: ComparisonOperand,
370}
371
372pickleable_new! {
373    impl Comparison {
374        pub fn new(
375            operator: ComparisonOperator,
376            destination: MemoryReference,
377            lhs: MemoryReference,
378            rhs: ComparisonOperand,
379        );
380    }
381}
382
383impl Quil for Comparison {
384    fn write(
385        &self,
386        f: &mut impl std::fmt::Write,
387        fall_back_to_debug: bool,
388    ) -> crate::quil::ToQuilResult<()> {
389        self.operator.write(f, fall_back_to_debug)?;
390        write!(f, " ")?;
391        self.destination.write(f, fall_back_to_debug)?;
392        write!(f, " ")?;
393        self.lhs.write(f, fall_back_to_debug)?;
394        write!(f, " ")?;
395        self.rhs.write(f, fall_back_to_debug)?;
396        Ok(())
397    }
398}
399
400#[derive(Clone, Debug)]
401#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
402#[cfg_attr(
403    feature = "python",
404    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all)
405)]
406pub enum ComparisonOperand {
407    LiteralInteger(i64),
408    LiteralReal(f64),
409    MemoryReference(MemoryReference),
410}
411
412impl Quil for ComparisonOperand {
413    fn write(
414        &self,
415        f: &mut impl std::fmt::Write,
416        fall_back_to_debug: bool,
417    ) -> crate::quil::ToQuilResult<()> {
418        match &self {
419            ComparisonOperand::LiteralInteger(value) => write!(f, "{value}").map_err(Into::into),
420            ComparisonOperand::LiteralReal(value) => write!(f, "{value}").map_err(Into::into),
421            ComparisonOperand::MemoryReference(value) => value.write(f, fall_back_to_debug),
422        }
423    }
424}
425
426impl PartialEq for ComparisonOperand {
427    fn eq(&self, other: &Self) -> bool {
428        match (self, other) {
429            (Self::LiteralInteger(this), Self::LiteralInteger(that)) => this == that,
430            (Self::LiteralReal(this), Self::LiteralReal(that)) => {
431                floating_point_eq::f64::eq(*this, *that)
432            }
433            (Self::MemoryReference(this), Self::MemoryReference(that)) => this == that,
434            // This explicit or-pattern ensures that we'll get a compilation error if
435            // `ComparisonOperand` grows another constructor.
436            (Self::LiteralInteger(_) | Self::LiteralReal(_) | Self::MemoryReference(_), _) => false,
437        }
438    }
439}
440
441impl Eq for ComparisonOperand {}
442
443impl std::hash::Hash for ComparisonOperand {
444    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
445        match self {
446            ComparisonOperand::LiteralInteger(operand) => operand.hash(state),
447            ComparisonOperand::LiteralReal(operand) => {
448                floating_point_eq::f64::hash(*operand, state)
449            }
450            ComparisonOperand::MemoryReference(operand) => operand.hash(state),
451        }
452    }
453}
454
455pub trait ClassicalOperand: Clone + std::fmt::Debug {
456    fn memory_reference(&self) -> Option<&MemoryReference>;
457}
458
459macro_rules! classical_operands {
460    ($($operand:ident),* $(,)?) => {
461        $(
462            impl ClassicalOperand for $operand {
463                fn memory_reference(&self) -> Option<&MemoryReference> {
464                    if let Self::MemoryReference(memory_reference) = self {
465                        Some(memory_reference)
466                    } else {
467                        None
468                    }
469                }
470            }
471        )*
472    }
473}
474
475classical_operands! { ArithmeticOperand, BinaryOperand, ComparisonOperand }
476
477#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
478#[cfg_attr(feature = "stubs", gen_stub_pyclass_enum)]
479#[cfg_attr(
480    feature = "python",
481    pyo3::pyclass(
482        module = "quil.instructions",
483        eq,
484        frozen,
485        hash,
486        rename_all = "SCREAMING_SNAKE_CASE"
487    )
488)]
489pub enum ComparisonOperator {
490    Equal,
491    GreaterThanOrEqual,
492    GreaterThan,
493    LessThanOrEqual,
494    LessThan,
495}
496
497impl Quil for ComparisonOperator {
498    fn write(
499        &self,
500        f: &mut impl std::fmt::Write,
501        _fall_back_to_debug: bool,
502    ) -> crate::quil::ToQuilResult<()> {
503        match &self {
504            ComparisonOperator::Equal => write!(f, "EQ"),
505            ComparisonOperator::GreaterThanOrEqual => write!(f, "GE"),
506            ComparisonOperator::GreaterThan => write!(f, "GT"),
507            ComparisonOperator::LessThanOrEqual => write!(f, "LE"),
508            ComparisonOperator::LessThan => write!(f, "LT"),
509        }
510        .map_err(Into::into)
511    }
512}
513
514#[derive(Clone, Debug, Hash, PartialEq, Eq)]
515#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
516#[cfg_attr(
517    feature = "python",
518    pyo3::pyclass(module = "quil.instructions", eq, frozen, hash, get_all, subclass)
519)]
520pub struct UnaryLogic {
521    pub operator: UnaryOperator,
522    pub operand: MemoryReference,
523}
524
525pickleable_new! {
526    impl UnaryLogic {
527        pub fn new(operator: UnaryOperator, operand: MemoryReference);
528    }
529}
530
531impl Quil for UnaryLogic {
532    fn write(
533        &self,
534        f: &mut impl std::fmt::Write,
535        fall_back_to_debug: bool,
536    ) -> crate::quil::ToQuilResult<()> {
537        self.operator.write(f, fall_back_to_debug)?;
538        write!(f, " ")?;
539        self.operand.write(f, fall_back_to_debug)?;
540        Ok(())
541    }
542}
543
544#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
545#[cfg_attr(feature = "stubs", gen_stub_pyclass_enum)]
546#[cfg_attr(
547    feature = "python",
548    pyo3::pyclass(
549        module = "quil.instructions",
550        eq,
551        frozen,
552        hash,
553        rename_all = "SCREAMING_SNAKE_CASE"
554    )
555)]
556pub enum UnaryOperator {
557    Neg,
558    Not,
559}
560
561impl Quil for UnaryOperator {
562    fn write(
563        &self,
564        f: &mut impl std::fmt::Write,
565        _fall_back_to_debug: bool,
566    ) -> crate::quil::ToQuilResult<()> {
567        match &self {
568            UnaryOperator::Neg => write!(f, "NEG"),
569            UnaryOperator::Not => write!(f, "NOT"),
570        }
571        .map_err(Into::into)
572    }
573}