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