qasmsim/grammar/
ast.rs

1//! Contain the data structures for creating OPENQASM ASTs. The module is
2//! **unstable**.
3//!
4//! # Notes
5//!
6//! Although OPENQASM 2.0 is stable enough, this module is not. Providing better
7//! errors would require an extensive use of the `Span` structure beyond
8//! statements, and adding new features to the language would require the
9//! modification os certain layouts.
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14use crate::grammar::lexer::Location;
15
16/// Represent a OPENQASM program. A valid program contains a version string
17/// and a list of instructions.
18///
19/// # Examples
20///
21/// The AST corresponding to the following program:
22///
23/// ```qasm
24/// OPENQASM 2.0;
25/// qreg q[1];
26/// U(pi/2, 0, pi) q[0];
27/// ```
28///
29/// Can be built programmatically with:
30///
31/// ```
32/// use qasmsim::grammar::ast::{
33///     OpenQasmProgram,
34///     Span,
35///     Statement,
36///     QuantumOperation,
37///     UnitaryOperation,
38///     OpCode,
39///     Expression,
40///     Argument
41/// };
42/// use qasmsim::grammar::lexer::Location;
43///
44/// let program = OpenQasmProgram {
45///     version: "2.0".to_string(),
46///     program: vec![
47///         Span {
48///             boundaries: (Location(14), Location(24)),
49///             node: Box::new(
50///                 Statement::QRegDecl(
51///                     "q".to_string(),
52///                     1
53///                 )
54///             )
55///         },
56///         Span {
57///             boundaries: (Location(25), Location(45)),
58///             node: Box::new(
59///                 Statement::QuantumOperation(
60///                     QuantumOperation::Unitary(
61///                         UnitaryOperation(
62///                             "U".to_string(),
63///                             vec![
64///                                 Expression::Op(
65///                                     OpCode::Div,
66///                                     Box::new(Expression::Pi),
67///                                     Box::new(Expression::Int(2))
68///                                 ),
69///                                 Expression::Int(0),
70///                                 Expression::Pi
71///                             ],
72///                             vec![
73///                                 Argument::Item("q".to_string(), 0)
74///                             ]
75///                         )
76///                     )
77///                 )
78///             )
79///         }
80///     ]
81/// };
82/// ```
83#[derive(Debug, Clone, PartialEq)]
84#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
85pub struct OpenQasmProgram {
86    /// The version of the language as in `X.Y`. Current supported version is
87    /// `2.0`.
88    pub version: String,
89    /// List of statements conforming the program body.
90    pub program: Vec<Span<Statement>>,
91}
92
93/// Represent a OPENQASM library. OPENQASM libraries can contain gate
94/// declarations only.
95///
96/// # Examples
97///
98/// The AST corresponding to the following library:
99///
100/// ```qasm
101/// gate h q {
102///     U(pi/2, 0, pi) q[0];
103/// }
104/// ```
105///
106/// Can be built programmatically with:
107///
108/// ```
109/// use qasmsim::grammar::ast::{
110///     OpenQasmLibrary,
111///     Statement,
112///     GateOperation,
113///     UnitaryOperation,
114///     OpCode,
115///     Expression,
116///     Argument
117/// };
118/// use qasmsim::grammar::lexer::Location;
119///
120/// let library = OpenQasmLibrary {
121///     definitions: vec![
122///         Statement::GateDecl {
123///             signature: (
124///                 "h".to_string(),
125///                 vec![],
126///                 vec!["q".to_string()],
127///                 vec![
128///                     GateOperation::Unitary(
129///                         UnitaryOperation(
130///                            "U".to_string(),
131///                             vec![
132///                                 Expression::Op(
133///                                     OpCode::Div,
134///                                     Box::new(Expression::Pi),
135///                                     Box::new(Expression::Int(2))
136///                                 ),
137///                                 Expression::Int(0),
138///                                 Expression::Pi
139///                             ],
140///                             vec![
141///                                 Argument::Item("q".to_string(), 0)
142///                             ]
143///                         )
144///                     )
145///                 ]
146///             ),
147///             docstring: None
148///         }
149///     ]
150/// };
151#[derive(Debug, Clone, PartialEq)]
152#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
153pub struct OpenQasmLibrary {
154    /// List of gate declarations. Although the type allows for the contruction
155    /// of a library with arbitrary statements, this would not constitute a
156    /// valid OPENQASM library and the linker would panic at runtime.
157    pub definitions: Vec<Statement>,
158}
159
160// TODO: This should not be part of the grammar. It is a directive for
161// the optimizer or compiler.
162
163/// A pragma for a potential gate optimizer to prevent the combination of the
164/// gates at both sides of the barrier. The barrier takes a list of registers
165/// or qubits arguments.
166///
167/// # Examples
168///
169/// The AST corresponding to the `barrier` of the following program:
170///
171/// ```qasm
172/// barrier q;
173/// ```
174///
175/// Corresponds to:
176///
177/// ```
178/// use qasmsim::grammar::ast::{BarrierPragma, Argument};
179///
180/// let barrier = BarrierPragma(vec![Argument::Id("q".to_string())]);
181/// ```
182///
183/// A `BarrierPragma` cannot compound a valid [`OpenQasmProgram`]. It needs
184/// to be enclosed in a [`Statement`].
185///
186/// [`OpenQasmProgram`]: ./struct.OpenQasmProgram.html
187/// [`Statement`]: ./enum.Statement.html
188#[derive(Debug, Clone, PartialEq, Eq, Hash)]
189#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
190pub struct BarrierPragma(pub Vec<Argument>);
191
192/// Each of the statements you can find in a OPENQASM program.
193///
194/// OPENQASM programs are made exclusively of list of statements. The statements
195/// can be wrappers for more complex structures and take parameters representing
196/// these structures.
197///
198/// # Examples
199///
200/// The following OPENQASM code:
201///
202/// ```qasm
203/// barrier q;
204/// ```
205///
206/// Is represented, as statement, like:
207///
208/// ```
209/// use qasmsim::grammar::ast::{Statement, BarrierPragma, Argument};
210///
211/// let barrier_stmt = Statement::Barrier(
212///     BarrierPragma(vec![Argument::Id("q".to_string())])
213/// );
214/// ```
215///
216/// Enclose a statement inside a [span] and aggregate them in a list to form
217/// a valid [`OpenQasmProgram`].
218///
219/// [span]: ./struct.Span.html
220/// [`OpenQasmProgram`]: ./struct.OpenQasmProgram.html
221#[non_exhaustive]
222#[derive(Debug, Clone, PartialEq)]
223#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
224pub enum Statement {
225    /// Quantum register declaration with name and size.
226    QRegDecl(String, usize),
227    /// Classical register declaration with name and size.
228    CRegDecl(String, usize),
229    /// Quantum gate declaration with signature and docstring, if any.
230    GateDecl {
231        /// The signature includes the name, list of formal real parameters,
232        /// list of formal quantum registers, and a list of [`GateOperation`]
233        /// representing the body of the gate.
234        signature: (String, Vec<String>, Vec<String>, Vec<GateOperation>),
235        /// A string representing documentation related to the gate.
236        docstring: Option<String>,
237    },
238    /// Include statement for linking with gate libraries.
239    Include(String),
240    /// A wrapper for the barrier pragma.
241    Barrier(BarrierPragma),
242    /// Opaque gate declaration with signature and docstring, if any.
243    OpaqueGateDecl {
244        /// The signature inlcudes the name and formal lists of real parameters
245        /// and quantum registers. Opaque declarations have no body.
246        signature: (String, Vec<String>, Vec<String>),
247        /// A string representing documentation related to the gate.
248        docstring: Option<String>,
249    },
250    /// A wrapper for a quantum operation.
251    QuantumOperation(QuantumOperation),
252    /// A wrapper for making a quantum operation to simulate just if certain
253    /// equality condition holds. The wrapper takes the left-side of the
254    /// comparison, the right side, and the operation to perform.
255    Conditional(Argument, u64, QuantumOperation),
256}
257
258/// Relates a node with the fragment of source code where the node appears.
259///
260/// # Examples
261///
262/// Consider the following program:
263///
264/// ```qasm
265/// OPENQASM 2.0;
266/// qreg q[10];
267/// ```
268///
269/// The span for the second line statement is as follows:
270///
271/// ```
272/// use qasmsim::grammar::ast::{Span, Statement};
273/// use qasmsim::grammar::lexer::Location;
274///
275/// let barrier_span = Span {
276///     boundaries: (Location(14), Location(25)),
277///     node: Box::new(
278///         Statement::QRegDecl(
279///             "q".to_string(),
280///             1
281///         )
282///     )
283/// };
284/// ```
285///
286/// Boundaries run from characters 14 to 25 corresponding to the starting-0
287/// character index of the source code.
288///
289/// Right, now, only statements are tied to spans making impossible to
290/// accurately localize inner AST nodes.
291#[derive(Debug, Clone, PartialEq)]
292#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
293pub struct Span<S> {
294    /// Pair of source locations where the AST node can be found.
295    pub boundaries: (Location, Location),
296    /// Boxed AST node.
297    pub node: Box<S>,
298}
299
300/// Any of the statements that can appear inside a gate definition.
301///
302/// # Examples
303///
304/// See [`OpenQasmLibrary`] for a complete example.
305///
306/// A gate in OPENQASM is always reversible so gates can only be compound of
307/// barriers (which are no-op actually) and other gate invocations.
308///
309/// [`OpenQasmLibrary`]: ./struct.OpenQasmLibrary.html
310#[non_exhaustive]
311#[derive(Debug, Clone, PartialEq)]
312#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
313pub enum GateOperation {
314    /// A gate invocation.
315    Unitary(UnitaryOperation),
316    /// A barrier pragma.
317    Barrier(BarrierPragma),
318}
319
320/// Any of the operations that actuates over quantum registers.
321///
322/// # Examples
323///
324/// See [`OpenQasmProgram`] for a complete examples.
325///
326/// [`OpenQasmProgram`]: ./struct.OpenQasmProgram.html
327#[non_exhaustive]
328#[derive(Debug, Clone, PartialEq)]
329#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
330pub enum QuantumOperation {
331    /// A gate invocation.
332    Unitary(UnitaryOperation),
333    /// A measurement on a quantum register to a classical register.
334    Measure(Argument, Argument),
335    /// A reset operation on a quantum register.
336    Reset(Argument),
337}
338
339/// A gate "invocation".
340///
341/// The name comes after the fact that all quantum gates are [unitary]
342/// operators. Calling a gate consists on applying it on some quantum
343/// registers.
344///
345/// # Examples
346///
347/// See [`OpenQasmProgram`] for a complete example.
348///
349/// [`OpenQasmProgram`]: ./struct.OpenQasmProgram.html
350/// [unitary]: https://en.wikipedia.org/wiki/Unitary_operator
351#[derive(Debug, Clone, PartialEq)]
352#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
353pub struct UnitaryOperation(pub String, pub Vec<Expression>, pub Vec<Argument>);
354
355/// Any of the operators that can appear in an expression.
356///
357/// # Examples
358///
359/// Notice the different `Expression` instances in the [`OpenQasmLibrary`]
360/// example.
361///
362/// [`OpenQasmLibrary`]: ./struct.OpenQasmLibrary.html
363#[non_exhaustive]
364#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
365#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
366pub enum OpCode {
367    /// Code for the addition operator `+`.
368    Add,
369    /// Code for the substraction operator `-`.
370    Sub,
371    /// Code for the multiplication operator `*`.
372    Mul,
373    /// Code for the division operator `/`.
374    Div,
375    /// Code for the power operator `^`.
376    Pow,
377}
378
379/// Any of the functions that can appear in an expression.
380#[non_exhaustive]
381#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
382#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
383pub enum FuncCode {
384    /// Function sinus `sin`.
385    Sin,
386    /// Function cosinus `cos`.
387    Cos,
388    /// Function tangent `tan`.
389    Tan,
390    /// Function exponential `exp`.
391    Exp,
392    /// Function natural logarithm `ln`.
393    Ln,
394    /// Function square root `sqrt`.
395    Sqrt,
396}
397
398/// Any of the subexpressions that can appear inside a expression.
399///
400/// # Examples
401///
402/// See [`OpenQasmLibrary`] for an example.
403///
404/// [`OpenQasmLibrary`]: ./struct.OpenQasmLibrary.html
405#[non_exhaustive]
406#[derive(Debug, Clone, PartialEq)]
407#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
408pub enum Expression {
409    /// The pi constant `pi`.
410    Pi,
411    /// A valid OPENQASM identifier.
412    Id(String),
413    /// A real number.
414    Real(f64),
415    /// An integer number.
416    Int(u64),
417    /// A binary operation.
418    Op(OpCode, Box<Expression>, Box<Expression>),
419    /// A call to a function.
420    Function(FuncCode, Box<Expression>),
421    /// A negation of an expression.
422    Minus(Box<Expression>),
423}
424
425/// A reference to a register or register component.
426///
427/// # Examples
428///
429/// Look at these barrier statements:
430///
431/// ```qasm
432/// barrier q;
433/// barrier q[0];
434/// ```
435///
436/// They differ on the quantum register argument and can be built with:
437///
438/// ```
439/// use qasmsim::grammar::ast::{BarrierPragma, Argument};
440///
441/// let on_the_whole_register = BarrierPragma(
442///     vec![Argument::Id("q".to_string())]
443/// );
444/// let on_the_first_qubit = BarrierPragma(
445///     vec![Argument::Item("q".to_string(), 0)]
446/// );
447/// ```
448#[non_exhaustive]
449#[derive(Debug, Clone, PartialEq, Eq, Hash)]
450#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
451pub enum Argument {
452    /// An entire register like `q`.
453    Id(String),
454    /// One of the bits/qubits of a register `q[0]`.
455    Item(String, usize),
456}