Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
Qasm.h
Go to the documentation of this file.
1
12
13#pragma once
14
15#ifndef _QASM_H_
16#define _QASM_H_
17
18#include "SyntaxTree.h"
19
20namespace qasm
21{
22
24{
25 template <typename, typename, typename> struct result
26 {
27 typedef void type;
28 };
29
30 template <typename Iterator> void operator()(qi::info const &what, Iterator err_pos, Iterator last) const
31 {
32 std::cout << "Error! Expecting " << what << " here: \"" << std::string(err_pos, last) << "\"\n";
33 }
34};
35
36inline phx::function<error_handler_> const error_handler = error_handler_();
37} // namespace qasm
38
39BOOST_FUSION_ADAPT_STRUCT(qasm::Program, (std::vector<std::string>, comments)(double, version)(std::vector<std::string>,
40 includes)(std::vector<qasm::StatementType>, statements))
41
42namespace qasm
43{
44
45inline void printd(const double &v)
46{
47 std::cout << "version: " << v << "\n";
48}
49
50inline void prints(const std::string &s)
51{
52 std::cout << "statement: " << s << "\n";
53}
54
55// TODO:
56// 1. 'opaque' will be parsed but ignored in the first phase.
57// 2. 'barrier' will be parsed, but ignored in the first phase. In this case we
58// might want to add a 'barrier' operation in our circuit. For now it's not
59// existent. Adding it would have implications in circuit execution with the
60// discrete event simulator and also in the transpiler functionality.
61
62template <typename Iterator> struct QasmSkipper : qi::grammar<Iterator>
63{
64 QasmSkipper() : QasmSkipper::base_type(skip)
65 {
66 // Block comments are accepted as whitespace in both dialects, including
67 // before a header, as a harmless compatibility extension.
68 blockComment = qi::lexeme[qi::lit("/*") >> *(qi::char_ - qi::lit("*/")) >> qi::lit("*/")];
69 skip = ascii::space | blockComment;
70 }
71
72 qi::rule<Iterator> skip;
73 qi::rule<Iterator> blockComment;
74};
75
76struct MakeSupportedVersionExpr
77{
78 template <typename, typename, typename> struct result
79 {
80 typedef double type;
81 };
82
83 // `VersionSpecifier: [0-9]+ ('.' [0-9]+)?` - the minor version is optional
84 // in QASM3, but QASM2 spells the field as `real`, which is never bare.
85 double operator()(unsigned int major, const boost::optional<unsigned int> &minor, bool &isQasm3) const
86 {
87 if (major != 2U && major != 3U)
88 throw std::invalid_argument("Unsupported OpenQASM major version " + std::to_string(major) + ". Only versions 2.x and 3.x are supported.");
89
90 if (major == 2U && !minor)
91 throw std::invalid_argument("OpenQASM 2 requires an explicit minor version, as in "
92 "'OPENQASM 2.0;'.");
93
94 isQasm3 = major == 3U;
95 if (!minor)
96 return static_cast<double>(major);
97
98 return std::stod(std::to_string(major) + "." + std::to_string(*minor));
99 }
100};
101
102inline phx::function<MakeSupportedVersionExpr> MakeSupportedVersion;
103
104struct ValidateIncludeExpr
105{
106 template <typename> struct result
107 {
108 typedef std::string type;
109 };
110
111 std::string operator()(const std::string &includeName) const
112 {
113 if (includeName != "qelib1.inc" && includeName != "stdgates.inc")
114 throw std::invalid_argument("Unsupported OpenQASM include '" + includeName +
115 "'. Only qelib1.inc and "
116 "stdgates.inc are recognized prelude markers.");
117 return includeName;
118 }
119};
120
121inline phx::function<ValidateIncludeExpr> ValidateInclude;
122
123template <typename Iterator = std::string::iterator, typename Skipper = QasmSkipper<Iterator>> struct QasmGrammar : qi::grammar<Iterator, Program(), Skipper>
124{
125 QasmGrammar() : QasmGrammar::base_type{program}
126 {
127 version = (qi::omit[qi::lexeme[qi::lit("OPENQASM") >> qi::space]] >> qi::lexeme[qi::uint_ >> -('.' >> qi::uint_)] >>
128 ';')[qi::_val = MakeSupportedVersion(qi::_1, qi::_2, phx::ref(isQasm3))];
129
130 // Keyword -> diagnostic message table for QASM3 constructs outside our
131 // supported subset. Table-driven rather than one rule per keyword, since
132 // every entry follows the same "recognise keyword, throw its message"
133 // shape (see `unsupportedConstruct` below).
134 unsupportedKeywords.add("for", "OpenQASM 3 'for' loops are not supported.")("while", "OpenQASM 3 'while' loops are not supported.")(
135 "def", "OpenQASM 3 subroutine definitions ('def') are not supported.")("let", "OpenQASM 3 register aliases ('let') are not supported.")(
136 "duration", "OpenQASM 3 duration declarations ('duration') are not supported.")("box", "OpenQASM 3 box blocks ('box') are not supported.")(
137 "array", "OpenQASM 3 array declarations ('array') are not supported.")("output", "OpenQASM 3 output declarations ('output') are not supported.")(
138 "const", "OpenQASM 3 constant declarations ('const') are not supported.")("extern",
139 "OpenQASM 3 external subroutines ('extern') are not supported.")(
140 "stretch", "OpenQASM 3 stretch declarations ('stretch') are not supported.")("pragma", "OpenQASM 3 pragma statements are not supported.")(
141 "defcal", "OpenQASM 3 calibration definitions ('defcal') are not supported.");
142
143 // The gate names that exist only in OpenQASM 3's stdgates.inc, kept as a
144 // symbol table for the same reason unsupportedKeywords is one: the
145 // recognition is a name lookup. Each name maps to itself, so the rule's
146 // attribute is the offending spelling and the diagnostic can quote it.
147 qasm3OnlyGates.add("phase", "phase")("cphase", "cphase")("gphase", "gphase");
148
149 durationUnit.add("ns", 1e-9)("us", 1e-6)("ms", 1e-3)("s", 1.0)("dt", -1.0);
150
151 comments %= *comment;
152 includes %= *include;
153
154 // An absent header intentionally retains the parser's historical QASM2
155 // compatibility mode; an explicit header selects and validates a dialect.
156 program = comments >> (-version) >> includes >> statements;
157
158 // condOpBraced is tried before the plain `statement` alternative (which
159 // contains the unbraced condOp) so that a braced conditional isn't first
160 // matched up to `if ( ... )` by condOp only to fail on '{'. condOpBraced
161 // synthesizes a std::vector<StatementType> (one conditioned statement per
162 // body qop) and is spliced in, while a plain statement is pushed as a
163 // single element - this is the flattening the braced multi-statement form
164 // requires.
165 statements =
166 *(condOpBraced[phx::insert(qi::_val, phx::end(qi::_val), phx::begin(qi::_1), phx::end(qi::_1))] | statement[phx::push_back(qi::_val, qi::_1)]);
167
168 statement = comment[qi::_val = AddComment(qi::_1)] | decl[qi::_val = AddDeclaration(qi::_1)] |
169 opaque[qi::_val = AddOpaqueDecl(qi::_1, std::ref(opaqueGates), std::ref(qreg_map), std::ref(declarations))] |
170 condOp[qi::_val = AddCondQop(qi::_1, std::ref(qreg_map), std::ref(creg_map), std::ref(opaqueGates), std::ref(definedGates))] |
171 gatedeclfull[qi::_val = AddGateDecl(qi::_1, std::ref(definedGates), std::ref(declarations))] |
172 inputdecl[qi::_val = AddInputDecl(qi::_1, std::ref(inputNames), std::ref(declarations), std::ref(inputBindings), std::ref(inputValues))] |
173 unsupportedConstruct[qi::_val = qi::_1] | qop[qi::_val = qi::_1];
174
175 // this is the opaque gate declaration, it will be simply ignored (in 3.0 is
176 // supposed to be ignored)
177
178 opaque %=
179 qi::omit[qi::lexeme[qi::lit("opaque") >> qi::space]] >> identifier >> (('(' >> idList >> ')') | ('(' >> qi::eps >> ')') | qi::eps) >> idList >> ';';
180
181 // **************************************************************************************************************************************************************
182
183 // some of the more complex things
184
185 gatedecl %=
186 qi::omit[qi::lexeme[qi::lit("gate") >> qi::space]] >> identifier >> (('(' >> idList >> ')') | ('(' >> qi::eps >> ')') | qi::eps) >> idList >> '{';
187
188 simplebarrier %= qi::omit[qi::lexeme[qi::lit("barrier") >> qi::space]] >> idList >> ';';
189 gateBodyModifier = (+modifier)[qi::_val = RejectGateBodyModifier(qi::_1)];
190 gatedeclop %= simplebarrier | (uop >> ';') | gateBodyModifier;
191
192 gatedeclfull %= gatedecl >> *gatedeclop >> '}';
193
194 // **************************************************************************************************************************************************************
195
196 // `==` and the `!` below are supported only in conditional heads; they are
197 // intentionally not part of the general expression grammar.
198 condOp %= qi::lit("if") >> '(' >> identifier >> qi::lit("==") >> qi::int_ >> ')' >> qop;
199
200 // The condition head inside `if ( ... )` for the QASM3 braced form. Two
201 // shapes, each with its own semantic action (per-alternative, not one
202 // action on the whole alternation - see the file-level Spirit lessons
203 // note) so CondHeadType can be constructed directly without needing a
204 // BOOST_FUSION_ADAPT_STRUCT for an asymmetric union of the two shapes:
205 // - register comparison: `c == 2`
206 // - negated bit: `!c[0]` -> bit must equal 0
207 // - bare bit: `c[0]` -> bit must equal 1
208 // - `&&` chain: `c0[0] && !c1[0]` -> every listed bit must match
209 // Register form is tried first only to mirror condOp/condOpBraced's
210 // pre-existing ordering; the two are unambiguous regardless of order
211 // since '==' vs '[' immediately disambiguate them after `identifier`.
212 condBitTest = (qi::lit('!') >> indexedId)[qi::_val = MakeCondBitTest(qi::_1, false)] | indexedId[qi::_val = MakeCondBitTest(qi::_1, true)];
213
214 // A `&&`-joined chain is what CircToQasm emits for a condition spanning
215 // more than one classical bit, so accepting it here is what makes
216 // circuit -> QASM3 -> circuit round-trip for those conditions.
217 condHead = (identifier >> qi::lit("==") >> qi::int_)[qi::_val = MakeRegCondHead(qi::_1, qi::_2)] |
218 (condBitTest % qi::lit("&&"))[qi::_val = MakeBitCondHead(qi::_1)];
219
220 // QASM3 braced conditional: `if (c == 1) { x q[0]; y q[1]; }`, plus the
221 // three additional Qiskit spellings this grammar accepts via condHead:
222 // `if (c[0]) { ... }`, `if (!c[0]) { ... }`, and an optional
223 // `else { ... }` clause. The else-clause is parsed for both condHead
224 // shapes - AddCondQopBraced is where a register-form else is rejected
225 // with a clear error, since accepting it here and only failing deep in
226 // circuit construction would be a worse diagnostic. The cbit-population
227 // logic is not duplicated here; it is delegated to AddCondQopExpr
228 // (register form) or done directly against the resolved single bit
229 // (bit form) inside AddCondQopBraced.
230 //
231 // `*qop` accepts zero repetitions, so `if (c == 1) { }` parses to an
232 // empty body vector, which splices in nothing at the call site in
233 // `statements` - i.e. it is accepted as a documented no-op, not rejected
234 // and not a way to silently drop the rest of the program. See
235 // QASM3EmptyBracedConditionalBodyIsANoOp in tests/Qasm.cpp.
236 condOpBraced = qi::eps(phx::ref(isQasm3)) >>
237 (qi::lit("if") >> '(' >> condHead >> ')' >> '{' >> *qop >> '}' >>
238 -(qi::lit("else") >> '{' >> *qop >> '}'))[qi::_val = AddCondQopBraced(qi::_1, qi::_2, qi::_3, std::ref(qreg_map), std::ref(creg_map),
239 std::ref(opaqueGates), std::ref(definedGates))];
240
241 // `gateCallStatement: ... (LPAREN expressionList? RPAREN)? ...` - an empty
242 // parameter list is legal and means the same as no parentheses at all.
243 // The shared `identifier` prefix is parsed exactly once, with the empty
244 // parens as an optional in the middle, rather than duplicated across two
245 // alternatives: a std::string attribute is not cleared when Qi backtracks
246 // out of a failed alternative, so the two-alternative form appended the
247 // identifier a second time and reported `x() q[0];` as a call to gate
248 // "xx". The optional is qi::omit-ed so the rule's attribute stays exactly
249 // SimpleGatecallType's (std::string, MixedListType) pair.
250 simpleGatecall %= identifier >> qi::omit[-(qi::lit('(') >> qi::lit(')'))] >> mixedList;
251 // 'gphase' is the sole zero-qubit call (global phase, e.g. "gphase(pi);"),
252 // so the trailing qubit list is optional here, defaulting to empty.
253 expGatecall %= identifier >> '(' >> expList >> ')' >> (mixedList | qi::attr(MixedListType()));
254
255 gatecall %= simpleGatecall | expGatecall;
256
257 // The trailing `!qi::lit(',')` on the two fixed-arity calls below stops
258 // them from swallowing the first arguments of a longer list and leaving
259 // the rest unparsable: `ctrl @ cx a, b, c` and `ctrl @ u(...) a, b` must
260 // fall through to `gatecall`, since Qi does not re-enter an alternative
261 // once a later element of the enclosing sequence fails. For unmodified
262 // QASM2 those over-long calls were parse errors before and are now
263 // reported by AddGateExpr as a qubit-count error instead.
264 // Lowercase `u` and `cx` are retained QASM2 compatibility aliases for the
265 // specification's uppercase `U` and `CX` builtins.
266 ugateCall %= (qi::lit("U") | qi::lit("u")) >> '(' >> expList >> ')' >> argument >> !qi::lit(',');
267 cxgateCall %= qi::omit[qi::lexeme[(qi::lit("CX") | qi::lit("cx")) >> qi::space]] >> argument >> ',' >> argument >> !qi::lit(',');
268
269 // The stdgates.inc-only gate names, rejected under a 2.0 header. The rule
270 // sits at the head of `uop` rather than at statement level so that every
271 // path to a gate call goes through it - a plain call, a modified call, a
272 // conditioned one, and a call inside a gate declaration body all reduce
273 // to `uop`. It is safe for a rule that may still backtrack to throw here
274 // because both of its guards have already fired by then: the dialect is
275 // QASM2, the name is one of the three (with an identifier-boundary
276 // lookahead, so "phased" is not "phase"), and the name is not one the
277 // program declared itself - that last case makes the rule fail instead,
278 // and the call falls through to `gatecall` below. See
279 // RejectQasm3OnlyGateExpr for why the filter lives here and not in the
280 // allowed-gate sets.
281 qasm2RejectedGate = qi::eps(!phx::ref(isQasm3)) >>
282 qi::lexeme[qasm3OnlyGates >> !qi::char_("a-zA-Z0-9_")][qi::_pass = RejectQasm3OnlyGate(qi::_1, std::ref(definedGates))];
283
284 uop %= qasm2RejectedGate | cxgateCall | ugateCall | gatecall;
285
286 // QASM3 gate modifiers. These rules stay pure - no semantic action that
287 // can throw - because they sit where the parse may still backtrack; the
288 // only throwing action is AddModifiedGate in `qop`, which runs once the
289 // choice is resolved. `modifiers` is zero-or-more, so `modifiedUop` with
290 // an empty list is exactly the plain `uop` it replaced in `qop`.
291 // `gateModifier: ... (CTRL | NEGCTRL) (LPAREN expression RPAREN)? AT` -
292 // the control count is optional and defaults to 1, so `ctrl(2) @ x a, b,
293 // c` is two controls. The count is folded into ModifierType::count rather
294 // than expanded into repeated modifiers here, so that one parsed modifier
295 // still maps to one ModifierType; AddModifiedGateExpr applies the
296 // lowering `count` times, which is what routes `ctrl(2) @ x` into ccx and
297 // `ctrl(3) @ x` into the existing multi-control error.
298 ctrlMod = (qi::lit("ctrl") >> -('(' >> expression >> ')') >> '@')[qi::_val = MakeCtrlModifier(ModifierKind::Ctrl, qi::_1, std::ref(inputValues))];
299 negctrlMod =
300 (qi::lit("negctrl") >> -('(' >> expression >> ')') >> '@')[qi::_val = MakeCtrlModifier(ModifierKind::NegCtrl, qi::_1, std::ref(inputValues))];
301 invMod %= qi::lit("inv") >> '@' >> qi::attr(ModifierType(ModifierKind::Inv));
302 powMod = (qi::lit("pow") >> '(' >> expression >> ')' >> '@')[qi::_val = MakePowModifier(qi::_1, std::ref(inputValues))];
303
304 modifier %= qi::eps(phx::ref(isQasm3)) >> (ctrlMod | negctrlMod | invMod | powMod);
305 modifiers %= *modifier;
306 modifiedUop %= modifiers >> uop;
307
308 qop = (measureOp[qi::_val = AddMeasure(qi::_1, std::ref(creg_map), std::ref(qreg_map))] |
309 measureAssignOp[qi::_val = AddMeasure(qi::_1, std::ref(creg_map), std::ref(qreg_map))] | measureNoTarget[qi::_val = qi::_1] |
310 resetOp[qi::_val = AddReset(qi::_1, std::ref(qreg_map))] | barrierOp[qi::_val = AddBarrier(qi::_1, std::ref(qreg_map))] |
311 delayOp[qi::_val = AddDelay(qi::_1, std::ref(qreg_map))] |
312 modifiedUop[qi::_val = AddModifiedGate(qi::_1, std::ref(qreg_map), std::ref(opaqueGates), std::ref(definedGates), std::ref(inputValues))]) >>
313 ';';
314
315 // **************************************************************************************************************************************************************
316
317 qregdecl %= (qi::omit[qi::lexeme[qi::lit("qreg") >> qi::space]] >>
318 indexedId)[qi::_val = AddQreg(std::ref(qreg_counter), std::ref(qreg_map), std::ref(declarations), qi::_1)];
319 cregdecl %= (qi::omit[qi::lexeme[qi::lit("creg") >> qi::space]] >>
320 indexedId)[qi::_val = AddCreg(std::ref(creg_counter), std::ref(creg_map), std::ref(declarations), qi::_1)];
321
322 // QASM3 register declarations: the size comes before the name, so we
323 // reuse MakeIndexedId with swapped placeholders instead of a new functor.
324 // Bare (size-1) forms are supported via qi::attr(1) standing in for the
325 // missing size. The sized form is tried first so it wins over the bare
326 // form on the shared "qubit"/"bit" prefix. Each alternative carries its
327 // own action (rather than one action on the alternative as a whole) so
328 // that qi::_1/qi::_2 are split from that alternative's own sequence
329 // attribute, matching the `expression` rule's convention below.
330 qubitdecl = qi::eps(phx::ref(isQasm3)) >>
331 ((qi::lit("qubit") >> '[' >> qi::int_ >> ']' >>
332 identifier)[qi::_val = AddQreg(std::ref(qreg_counter), std::ref(qreg_map), std::ref(declarations), MakeIndexedId(qi::_2, qi::_1))] |
333 (qi::omit[qi::lexeme[qi::lit("qubit") >> qi::space]] >> qi::attr(1) >>
334 identifier)[qi::_val = AddQreg(std::ref(qreg_counter), std::ref(qreg_map), std::ref(declarations), MakeIndexedId(qi::_2, qi::_1))]);
335 bitdecl = qi::eps(phx::ref(isQasm3)) >>
336 ((qi::lit("bit") >> '[' >> qi::int_ >> ']' >>
337 identifier)[qi::_val = AddCreg(std::ref(creg_counter), std::ref(creg_map), std::ref(declarations), MakeIndexedId(qi::_2, qi::_1))] |
338 (qi::omit[qi::lexeme[qi::lit("bit") >> qi::space]] >> qi::attr(1) >>
339 identifier)[qi::_val = AddCreg(std::ref(creg_counter), std::ref(creg_map), std::ref(declarations), MakeIndexedId(qi::_2, qi::_1))]);
340
341 decl %= (qregdecl | cregdecl | qubitdecl | bitdecl) >> ';';
342
343 // QASM3 free-parameter declaration: `input <type> <name>;`. Must be tried
344 // before `qop` in `statement`, or gatecall's identifier would match
345 // "input" as a gate name. The type keyword and its optional bit-width
346 // qualifier ("float[64]", "int[32]", "uint", "bool", "angle", ...) are
347 // retained so AddInputDecl can validate and normalize the API's double
348 // carrier before publishing the value to expression evaluation.
349 // The type keyword's `!qi::char_(...)` lookahead is wrapped in its own
350 // qi::lexeme, like "input"'s own keyword guard above, so a name that
351 // merely starts with a type keyword (e.g. "intx") cannot be mistaken for
352 // the keyword itself - the lookahead must run before the skipper can eat
353 // any whitespace, or it would never see the very next character.
354 inputType %=
355 qi::lexeme[(qi::string("float") | qi::string("int") | qi::string("uint") | qi::string("bool") | qi::string("angle")) >> !qi::char_("a-zA-Z0-9_")];
356 inputdecl %=
357 qi::eps(phx::ref(isQasm3)) >> qi::omit[qi::lexeme[qi::lit("input") >> qi::space]] >> inputType >> -('[' >> qi::int_ >> ']') >> identifier >> ';';
358
359 // QASM3 constructs outside our supported subset. Tried before `qop` in
360 // `statement`, or gatecall's identifier would swallow the keyword as a
361 // gate name and report the generic "Unsupported gate" error instead of
362 // naming the actual construct. Guard A: gated on isQasm3, since these
363 // words are ordinary identifiers in QASM2 - "for" is a fine register or
364 // gate name there. Guard B: the keyword-boundary lookahead sits inside
365 // the same qi::lexeme as the symbol lookup, following the `inputdecl`
366 // idiom above, so a name that merely starts with a reserved word (e.g.
367 // "format", "delayed") is not mistaken for the keyword itself. Throwing
368 // here is only safe because of this placement plus both guards - see
369 // RejectUnsupportedConstructExpr.
370 unsupportedConstruct =
371 qi::eps(phx::ref(isQasm3)) >> qi::lexeme[unsupportedKeywords >> !qi::char_("a-zA-Z0-9_")][qi::_val = RejectUnsupportedConstruct(qi::_1)];
372
373 measureOp %= qi::omit[qi::lexeme[qi::lit("measure") >> qi::space]] >> argument >> qi::lit("->") >> argument;
374 // QASM3 assignment-style measurement: `c[0] = measure q[0];`. The
375 // classical target is parsed first, so the (cbits, qubits) pair is
376 // swapped in-place via phx::construct into the same MeasureType consumed
377 // by AddMeasure above, avoiding a duplicate of AddMeasureExpr's
378 // indexed/whole-register handling.
379 measureAssignOp = qi::eps(phx::ref(isQasm3)) >> (argument >> '=' >> qi::omit[qi::lexeme[qi::lit("measure") >> qi::space]] >>
380 argument)[qi::_val = phx::construct<MeasureType>(qi::_2, qi::_1)];
381 // `measureArrowAssignmentStatement: measureExpression (ARROW
382 // indexedIdentifier)? SEMICOLON` - the arrow target is optional, i.e.
383 // `measure q;` means "measure and discard". This IR has no such
384 // operation: Circuits::CircuitFactory::CreateMeasurement takes
385 // (qubit, classical bit) pairs and every measurement writes a bit. Rather
386 // than invent a classical bit to hold a result the program never asked
387 // for, the form is recognised and rejected by name - the point of the
388 // rule is that the diagnostic says "measurement without a classical
389 // target" instead of the misleading "Unsupported gate without parameters:
390 // measure" the fall-through to `gatecall` used to produce. Tried after
391 // both real measurement rules, so it only ever sees a genuinely
392 // target-less measurement.
393 measureNoTarget = (qi::omit[qi::lexeme[qi::lit("measure") >> qi::space]] >> argument)[qi::_val = RejectMeasureWithoutTarget(qi::_1)];
394 resetOp %= qi::omit[qi::lexeme[qi::lit("reset") >> qi::space]] >> argument;
395 // `barrierStatement: BARRIER gateOperandList? SEMICOLON` - the operand
396 // list is optional and a bare `barrier;` applies to every qubit. The
397 // empty MixedListType stands for exactly that, and AddBarrierExpr expands
398 // it over the whole qreg map; `mixedList` matches one operand at minimum,
399 // so an empty list cannot arrive from the first alternative. The
400 // identifier-boundary lookahead keeps a gate named e.g. "barriers" from
401 // matching the bare form's keyword prefix.
402 //
403 // QASM3 only: QASM2's grammar is `statement: "barrier" anylist ";"`, with
404 // the operand list required, so the bare alternative is gated on isQasm3
405 // and `barrier;` under a 2.0 header is the syntax error it is there. The
406 // operand form is shared by both dialects and stays ungated.
407 barrierOp %= (qi::omit[qi::lexeme[qi::lit("barrier") >> qi::space]] >> mixedList) |
408 (qi::eps(phx::ref(isQasm3)) >> qi::omit[qi::lexeme[qi::lit("barrier") >> !qi::char_("a-zA-Z0-9_")]] >> qi::attr(MixedListType()));
409
410 delayOp = (qi::omit[qi::lexeme[qi::lit("delay") >> (qi::space | &qi::char_("[("))]] >>
411 (('[' >> expression >> -durationUnit >> ']') | ('(' >> expression >> -durationUnit >> ')')) >>
412 mixedList)[qi::_val = MakeDelay(qi::_1, qi::_2, std::ref(inputValues))];
413
414 // **************************************************************************************************************************************************************
415
416 idList %= identifier % ',';
417
418 indexedId = (identifier >> '[' >> qi::int_ >> ']')[qi::_val = MakeIndexedId(qi::_1, qi::_2)];
419
420 argument %= indexedId | identifier;
421 mixedList %= argument % ',';
422
423 // **************************************************************************************************************************************************************
424 // expressions
425
426 expList %= expression % ',';
427
428 // '^' is exponentiation in QASM2 but bitwise XOR in QASM3, which also
429 // introduces '**' for exponentiation:
430 // expression: <assoc=right> expression DOUBLE_ASTERISK expression
431 // | expression CARET expression
432 // The two spellings do not merely differ in name, they sit at opposite
433 // ends of the precedence table - '**' binds tighter than '*', while XOR
434 // binds looser than '+' - so they cannot share a rule. Hence:
435 // - `expression` (this level, loosest) carries the QASM3-only XOR;
436 // - `additive` is the former top of the chain, unchanged;
437 // - `factor2` (tightest binary level) carries the QASM3-only '**' and
438 // '^'-as-power only in QASM2.
439 // Every one of the three rules is guarded on isQasm3, so QASM2 semantics
440 // are exactly what QASM2 says they are: `2 ^ 3` is 8 there and 1 (2 XOR 3)
441 // here, and '**' - which QASM2's `exp` production does not have at all -
442 // is a syntax error under a 2.0 header.
443 expression =
444 (qi::eps(phx::ref(isQasm3)) >> additive >> qi::lit('^') >> expression)[qi::_val = MakeBinary('X', qi::_1, qi::_2)] | additive[qi::_val = qi::_1];
445
446 // Left-associative, as '-' and '/' are in every dialect of QASM and in
447 // ordinary arithmetic: `1 - 2 - 3` is (1 - 2) - 3 = -4 and `8 / 4 / 2` is
448 // (8 / 4) / 2 = 1. Both rules used to recurse into themselves on the
449 // right, which made them right-associative and silently produced 2 and 4
450 // instead - a wrong gate angle with no error at all. The fix is the
451 // standard Spirit left fold: parse one operand into qi::_val, then
452 // accumulate each following (operator, operand) pair onto it, so the left
453 // operand of each new node is the whole accumulated left-hand side rather
454 // than the first operand alone. '+' and '*' are associative and so
455 // numerically unaffected, but all four operators go through the same fold
456 // to keep one shape per precedence level.
457 additive = product[qi::_val = qi::_1] >> *(qi::char_("+-") >> product)[qi::_val = MakeBinary(qi::_1, qi::_val, qi::_2)];
458 product = unary[qi::_val = qi::_1] >> *(qi::char_("*/") >> unary)[qi::_val = MakeBinary(qi::_1, qi::_val, qi::_2)];
459
460 // The official grammar orders parenthesis > index > '**' (right-assoc) >
461 // unary > '* / %' > '+ -', i.e. a leading sign binds *looser* than '**':
462 // `-2 ** 2` is -(2 ** 2) = -4 and `2 ** -3 ** 2` is 2 ** -(3 ** 2). So
463 // `unary` - not `factor2` - is the operand of '*' and '/' above, and it
464 // applies the sign to the result of a whole power expression. It recurses
465 // into itself so repeated signs (`--2`) still work, and so the operand of
466 // a sign is itself allowed to be a power.
467 unary = (qi::char_("+-") >> unary)[qi::_val = MakeUnary(qi::_1, qi::_2)] | factor2[qi::_val = qi::_1];
468
469 // Right-associative, per DOUBLE_ASTERISK's <assoc=right> above: `2 ** 3 **
470 // 2` is 2 ** 9 = 512. The right operand is `unary` rather than `factor2`
471 // so that both the right-associativity and the spec's `2 ** -3` are
472 // expressible; the left operand is `factor`, which carries no sign of its
473 // own - a sign there would have to have been consumed by `unary` first,
474 // one precedence level out. '**' is a QASM3 powerExpression and has no
475 // counterpart in QASM2's `exp` production, so it is gated on isQasm3; the
476 // '^'-as-power alternative below is its exact complement, which is why
477 // the two can be ordered either way without `2 ** 3` ever being read as
478 // `2 ^ (* 3)`.
479 factor2 = (qi::eps(phx::ref(isQasm3)) >> factor >> qi::lit("**") >> unary)[qi::_val = MakeBinary('^', qi::_1, qi::_2)] |
480 (qi::eps(!phx::ref(isQasm3)) >> factor >> qi::lit('^') >> unary)[qi::_val = MakeBinary('^', qi::_1, qi::_2)] | factor[qi::_val = qi::_1];
481 factor = group[qi::_val = qi::_1] | constant[qi::_val = qi::_1] | (funcName >> group)[qi::_val = MakeFunction(qi::_1, qi::_2)] |
482 identifier[qi::_val = MakeVariable(qi::_1)];
483 // Unsigned on purpose. qi::double_ (and qi::int_) consume a leading sign
484 // themselves, so a signed number parsed here would attach the sign to the
485 // *base* of a power - `-2 ** 2` would be (-2) ** 2 = 4 - defeating the
486 // precedence the `unary` rule above establishes. Every sign is the
487 // `unary` rule's business; this parser only ever sees the digits. The
488 // former qi::int_ alternative is gone with it: it was unreachable (the
489 // real parser already matches an integer literal) and would have been
490 // another way for a sign to slip in below `unary`.
491 constant = qi::real_parser<double, qi::ureal_policies<double>>()[qi::_val = MakeConstant(qi::_1)] | pi[qi::_val = MakeConstant(qi::_1)];
492 group %= '(' >> expression >> ')';
493
494 funcName %= qi::string("sin") | qi::string("cos") | qi::string("tan") | qi::string("exp") | qi::string("ln") | qi::string("sqrt");
495 pi %= qi::lit("pi")[qi::_val = M_PI];
496
497 // **************************************************************************************************************************************************************
498
499 // very basic stuff
500 comment %= qi::lexeme[qi::lit("//") >> *(qi::char_ - qi::eol) >> -qi::eol];
501 quoted_string %= qi::lexeme['"' >> +(qi::char_ - '"') >> '"'];
502 // Includes are validated prelude markers only; this parser never loads an
503 // external file, and the program rule permits markers only at the start.
504 include = (qi::omit[qi::lexeme[qi::lit("include") >> qi::space]] >> quoted_string >> ';')[qi::_val = ValidateInclude(qi::_1)];
505 // The two dialects differ on the *first* character of an identifier and
506 // nowhere else. QASM2's lexical rule is `id := [a-z][A-Za-z0-9_]*`, i.e.
507 // the initial character must be a lowercase letter; QASM3's is
508 // `Identifier: [A-Za-z_][A-Za-z0-9_]*`, which also admits an uppercase
509 // letter or an underscore. So the first character is an alternation gated
510 // on isQasm3 while the trailing class stays shared. The whole thing
511 // remains a single qi::lexeme - the dialect choice is spelled inline
512 // rather than delegated to a sub-rule precisely so that no skipper can
513 // run between the first character and the rest, which would let
514 // `_ q` parse as the identifier "_q" under QASM3.
515 //
516 // QASM2's genuinely uppercase builtins are unaffected: `U` and `CX` are
517 // keywords there, matched by qi::lit in `ugateCall`/`cxgateCall`, not by
518 // this rule.
519 identifier %= qi::lexeme[((qi::eps(phx::ref(isQasm3)) >> qi::char_("a-zA-Z_")) | qi::char_("a-z")) >> *qi::char_("a-zA-Z0-9_")];
520
521 // Debugging and error handling and reporting support.
522 BOOST_SPIRIT_DEBUG_NODE(version);
523 BOOST_SPIRIT_DEBUG_NODE(program);
524 BOOST_SPIRIT_DEBUG_NODE(statement);
525 BOOST_SPIRIT_DEBUG_NODE(statements);
526
527 BOOST_SPIRIT_DEBUG_NODE(opaque);
528
529 BOOST_SPIRIT_DEBUG_NODE(gatedecl);
530 BOOST_SPIRIT_DEBUG_NODE(simplebarrier);
531 BOOST_SPIRIT_DEBUG_NODE(gateBodyModifier);
532 BOOST_SPIRIT_DEBUG_NODE(gatedeclop);
533 BOOST_SPIRIT_DEBUG_NODE(gatedeclfull);
534
535 BOOST_SPIRIT_DEBUG_NODE(condOp);
536 BOOST_SPIRIT_DEBUG_NODE(condBitTest);
537 BOOST_SPIRIT_DEBUG_NODE(condHead);
538 BOOST_SPIRIT_DEBUG_NODE(condOpBraced);
539 BOOST_SPIRIT_DEBUG_NODE(simpleGatecall);
540 BOOST_SPIRIT_DEBUG_NODE(expGatecall);
541 BOOST_SPIRIT_DEBUG_NODE(gatecall);
542 BOOST_SPIRIT_DEBUG_NODE(qasm2RejectedGate);
543 BOOST_SPIRIT_DEBUG_NODE(ugateCall);
544 BOOST_SPIRIT_DEBUG_NODE(cxgateCall);
545 BOOST_SPIRIT_DEBUG_NODE(uop);
546 BOOST_SPIRIT_DEBUG_NODE(ctrlMod);
547 BOOST_SPIRIT_DEBUG_NODE(negctrlMod);
548 BOOST_SPIRIT_DEBUG_NODE(invMod);
549 BOOST_SPIRIT_DEBUG_NODE(powMod);
550 BOOST_SPIRIT_DEBUG_NODE(modifier);
551 BOOST_SPIRIT_DEBUG_NODE(modifiers);
552 BOOST_SPIRIT_DEBUG_NODE(modifiedUop);
553 BOOST_SPIRIT_DEBUG_NODE(qop);
554
555 BOOST_SPIRIT_DEBUG_NODE(qregdecl);
556 BOOST_SPIRIT_DEBUG_NODE(cregdecl);
557 BOOST_SPIRIT_DEBUG_NODE(qubitdecl);
558 BOOST_SPIRIT_DEBUG_NODE(bitdecl);
559 BOOST_SPIRIT_DEBUG_NODE(decl);
560 BOOST_SPIRIT_DEBUG_NODE(inputdecl);
561 BOOST_SPIRIT_DEBUG_NODE(unsupportedConstruct);
562 BOOST_SPIRIT_DEBUG_NODE(resetOp);
563 BOOST_SPIRIT_DEBUG_NODE(measureOp);
564 BOOST_SPIRIT_DEBUG_NODE(measureAssignOp);
565 BOOST_SPIRIT_DEBUG_NODE(measureNoTarget);
566 BOOST_SPIRIT_DEBUG_NODE(barrierOp);
567
568 BOOST_SPIRIT_DEBUG_NODE(idList);
569 BOOST_SPIRIT_DEBUG_NODE(indexedId);
570 BOOST_SPIRIT_DEBUG_NODE(argument);
571 BOOST_SPIRIT_DEBUG_NODE(mixedList);
572
573 BOOST_SPIRIT_DEBUG_NODE(expList);
574
575 BOOST_SPIRIT_DEBUG_NODE(expression);
576 BOOST_SPIRIT_DEBUG_NODE(additive);
577 BOOST_SPIRIT_DEBUG_NODE(product);
578 BOOST_SPIRIT_DEBUG_NODE(factor2);
579 BOOST_SPIRIT_DEBUG_NODE(unary);
580 BOOST_SPIRIT_DEBUG_NODE(factor);
581 BOOST_SPIRIT_DEBUG_NODE(constant);
582 BOOST_SPIRIT_DEBUG_NODE(group);
583 BOOST_SPIRIT_DEBUG_NODE(funcName);
584 BOOST_SPIRIT_DEBUG_NODE(pi);
585
586 BOOST_SPIRIT_DEBUG_NODE(comment);
587 BOOST_SPIRIT_DEBUG_NODE(quoted_string);
588 BOOST_SPIRIT_DEBUG_NODE(include);
589 BOOST_SPIRIT_DEBUG_NODE(identifier);
590
591 // Error handling
592 qi::on_error<qi::fail>(expression, error_handler(qi::_4, qi::_3, qi::_2));
593 // TODO: add more error handlers if needed
594 qi::on_error<qi::fail>(program, error_handler(qi::_4, qi::_3, qi::_2));
595 }
596
597 void clear()
598 {
599 creg_counter = 0;
600 qreg_counter = 0;
601 creg_map.clear();
602 qreg_map.clear();
603 declarations.clear();
604 opaqueGates.clear();
605 definedGates.clear();
606 inputNames.clear();
607 inputBindings.clear();
608 inputValues.clear();
609 isQasm3 = false;
610 }
611
612 qi::rule<Iterator, Program(), Skipper> program;
613
614 qi::rule<Iterator, double(), Skipper> version;
615
616 qi::rule<Iterator, StatementType, Skipper> statement;
617 qi::rule<Iterator, std::vector<StatementType>(), Skipper> statements;
618
619 qi::rule<Iterator, OpaqueDeclType(), Skipper> opaque;
620
621 qi::rule<Iterator, GateDeclType(), Skipper> gatedecl;
622 qi::rule<Iterator, SimpleBarrierType(), Skipper> simplebarrier;
623 qi::rule<Iterator, UopType(), Skipper> gateBodyModifier;
624 qi::rule<Iterator, GateDeclOpType(), Skipper> gatedeclop;
625 qi::rule<Iterator, boost::fusion::vector<GateDeclType, std::vector<GateDeclOpType>>(), Skipper> gatedeclfull;
626
627 qi::rule<Iterator, CondOpType(), Skipper> condOp;
628 qi::rule<Iterator, CondBitTest(), Skipper> condBitTest;
629 qi::rule<Iterator, CondHeadType(), Skipper> condHead;
630 qi::rule<Iterator, std::vector<StatementType>(), Skipper> condOpBraced;
631
632 qi::rule<Iterator, UGateCallType, Skipper> ugateCall;
633 qi::rule<Iterator, CXGateCallType, Skipper> cxgateCall;
634
635 qi::rule<Iterator, SimpleGatecallType(), Skipper> simpleGatecall;
636 qi::rule<Iterator, ExpGatecallType(), Skipper> expGatecall;
637 qi::rule<Iterator, GatecallType(), Skipper> gatecall;
638 qi::rule<Iterator, UopType(), Skipper> qasm2RejectedGate;
639 qi::rule<Iterator, UopType(), Skipper> uop;
640
641 qi::rule<Iterator, ModifierType(), Skipper> ctrlMod;
642 qi::rule<Iterator, ModifierType(), Skipper> negctrlMod;
643 qi::rule<Iterator, ModifierType(), Skipper> invMod;
644 qi::rule<Iterator, ModifierType(), Skipper> powMod;
645 qi::rule<Iterator, ModifierType(), Skipper> modifier;
646 qi::rule<Iterator, ModifierListType(), Skipper> modifiers;
647 qi::rule<Iterator, ModifiedUopType(), Skipper> modifiedUop;
648
649 qi::rule<Iterator, QopType(), Skipper> qop;
650
651 qi::rule<Iterator, IndexedId(), Skipper> qregdecl;
652 qi::rule<Iterator, IndexedId(), Skipper> cregdecl;
653 qi::rule<Iterator, IndexedId(), Skipper> qubitdecl;
654 qi::rule<Iterator, IndexedId(), Skipper> bitdecl;
655 qi::rule<Iterator, IndexedId(), Skipper> decl;
656
657 qi::rule<Iterator, std::string(), Skipper> inputType;
658 qi::rule<Iterator, InputDeclType(), Skipper> inputdecl;
659
660 qi::rule<Iterator, StatementType, Skipper> unsupportedConstruct;
661
662 qi::rule<Iterator, ResetType(), Skipper> resetOp;
663 qi::rule<Iterator, MeasureType(), Skipper> measureOp;
664 qi::rule<Iterator, MeasureType(), Skipper> measureAssignOp;
665 qi::rule<Iterator, QopType(), Skipper> measureNoTarget;
666 qi::rule<Iterator, BarrierType(), Skipper> barrierOp;
667 qi::rule<Iterator, DelayType(), Skipper> delayOp;
668
669 qi::rule<Iterator, std::vector<std::string>(), Skipper> idList;
670
671 qi::rule<Iterator, IndexedId(), Skipper> indexedId;
672
673 qi::rule<Iterator, ArgumentType(), Skipper> argument;
674 qi::rule<Iterator, MixedListType(), Skipper> mixedList;
675
676 qi::rule<Iterator, std::vector<Expression>(), Skipper> expList;
677
678 // `unary` is an Expression like every other level: it is the whole
679 // "optionally signed power expression" level, not just the signed form, so
680 // it must be able to carry through the unsigned case as well.
681 qi::rule<Iterator, Expression(), Skipper> expression, additive, group, product, factor, factor2, unary;
682 qi::rule<Iterator, Constant(), Skipper> constant;
683
684 qi::rule<Iterator, std::string(), Skipper> funcName;
685 qi::rule<Iterator, std::string(), Skipper> comment;
686 qi::rule<Iterator, std::vector<std::string>(), Skipper> comments;
687 qi::rule<Iterator, std::string(), Skipper> include;
688 qi::rule<Iterator, std::vector<std::string>(), Skipper> includes;
689 qi::rule<Iterator, std::string(), Skipper> quoted_string;
690 qi::rule<Iterator, std::string(), Skipper> identifier;
691 qi::rule<Iterator, double(), Skipper> pi;
692
693 // Keyword -> diagnostic message table backing `unsupportedConstruct`.
694 qi::symbols<char, std::string> unsupportedKeywords;
695
696 // The stdgates.inc-only gate names backing `qasm2RejectedGate`; each maps
697 // to its own spelling.
698 qi::symbols<char, std::string> qasm3OnlyGates;
699 qi::symbols<char, double> durationUnit;
700
701 int creg_counter = 0;
702 int qreg_counter = 0;
703
704 bool isQasm3 = false;
705
706 std::unordered_map<std::string, IndexedId> creg_map;
707 std::unordered_map<std::string, IndexedId> qreg_map;
708 DeclarationRegistry declarations;
709
710 std::unordered_map<std::string, StatementType> opaqueGates;
711 std::unordered_map<std::string, StatementType> definedGates;
712
713 // Raw caller bindings are separate from values made visible by declarations,
714 // so QASM2, undeclared names, and forward references cannot consume them.
715 std::vector<std::string> inputNames;
716 std::unordered_map<std::string, double> inputBindings;
717 std::unordered_map<std::string, double> inputValues;
718
719 void ValidateInputBindings() const
720 {
721 for (const auto &[name, value] : inputBindings)
722 {
723 (void)value;
724 if (std::find(inputNames.begin(), inputNames.end(), name) == inputNames.end())
725 throw std::invalid_argument("No input declaration for binding '" + name + "'.");
726 }
727 }
728};
729
730} // namespace qasm
731
732#endif // !_QASM_H_
BOOST_FUSION_ADAPT_STRUCT(qasm::Program,(std::vector< std::string >, comments)(double, version)(std::vector< std::string >, includes)(std::vector< qasm::StatementType >, statements)) namespace qasm
Definition Qasm.h:39
phx::function< error_handler_ > const error_handler
Definition Qasm.h:36
void operator()(qi::info const &what, Iterator err_pos, Iterator last) const
Definition Qasm.h:30