Release 1.1.0 (August 17, 2026)
Summary
Added
- Added support for
#pragmastatements, which previously raisedValidationError: Unsupported statement of type <class 'openqasm3.ast.Pragma'>and blocked any program carrying one. Pragmas are now passed throughloads/validate/unroll/dumpsunchanged, and are printed in their#pragmaform (the upstream printer emits the barepragmakeyword). A#pragma braket verbatimadditionally marks theboxthat immediately follows it: gates inside a verbatim box are emitted as written instead of being decomposed, so verbatim submissions to Braket QPUs keep the native gates they were built with. (#341)
Improved / Modified
- Added / updated type hinting for
base.py,qasm2.py,qasm3.py, andvisitor.pysignatures, plus thevisit_statement/visit_basic_blocksignatures inpulse/visitor.py. Fixed grammatical typos inbase.py,qasm2.py,qasm3.py, andvisitor.pydocstrings. Fixes incorrect return types in the docstrings ofvisitor.py. (#346) - Added / updated type hinting for
pulse/visitor.pysignatures and fixes incorrect return types in the docstrings ofpulse/visitor.py. (#365) - Breaking:
loads()andload()now raiseTypeErroron an unrecognised keyword argument instead of silently ignoring it, so a misspelling likeloads(src, devise_qubits=5)fails at the call site rather than being dropped. Any caller currently passing a stray or misspelled kwarg will break on upgrade. (#356)
Deprecated
Removed
Fixed
- Fixed
loads()silently discarding a falsy kwarg value. The seven documented kwargs were stored behind a walrus truthiness test, so a caller value of0,0.0,False,{}or[]was treated as though the kwarg had never been passed. Each kwarg is now tested for presence, with an explicitNonestill meaning "not passed". What that changes per kwarg:extern_functions={}/[]is now stored rather than dropped; a non-positivedevice_qubits/device_cycle_time/compiler_angle_type_size/frame_limit_per_portis now rejected with aValueErrorat the call site rather than silently dropped, along with a non-numeric orboolvalue (TypeError); andframe_in_def_cal=None/play_in_cal_block=Nonenow mean "not passed" — previously an explicitNonewas stored and read as falsy, so passingNonedisabled the check that theTruedefault enables. PassFalsefor the oldNonebehaviour. (#356) - Fixed
unroll(consolidate_qubits=True)emitting two unrelated address spaces for a program mixing declared registers with physical qubits — a consolidated register plus as-written$nreferences. Such programs now raise aValidationErrornaming the physical qubits, and a program using only physical qubits no longer receives an internal register declaration nothing references. (#353) - Fixed external and verbatim-box gates counting the depth of the decomposition they skipped:
unroll(external_gates=["crz"])on a singlecrzreporteddepth() == 12while emitting one statement. An external gate now records its own depth, matching how a single-level external custom gate is already handled. (#352) - Fixed
has_measurements()/remove_measurements()andhas_barriers()/remove_barriers()missing occurrences insidefor/while/switchbodies on a module that has not been unrolled — e.g. a measurement inside aforloop was invisible andremove_measurements()was a no-op. The statement walker now descends into loop and switch bodies. (#354) - Fixed unrolling of
rzz/rxxin an OpenQASM 2 program emitting an invalidgphase(...)statement — syntax QASM 2 does not have — so the output was not a loadable QASM 2 program. Global phase is unobservable, so unroll-emitted phases are now dropped for a QASM 2 target; a user-writtengphaseis still rejected. A conditional left with no body by the drop is removed too, since QASM 2 has no form for anifwithout aqop. (#351) - Fixed the
negctrl @expansion emitting the samexQuantumGateobject at both the leading and trailing position, so in-place transformations mutated its operands twice — crashingreverse_qubit_order()(KeyError) andunroll(consolidate_qubits=True)(KeyError: '__PYQASM_QUBITS__') on any unrollednegctrlgate. The twoxgates are now distinct statements with fresh operand nodes. (#350) - Fixed inaccurate
device_qubitsentry inQasmModule.unroll()docstring (#349) - Fixed
remove_idle_qubits()andreverse_qubit_order()ignoring statements nested insideboxandifblocks. Top-level operands were rewritten while nested ones kept their old indices, so the result silently addressed the wrong qubits — and when a nested index fell outside the shrunken register, the output was not a loadable program at all. Both passes now walk nested bodies, as dohas_measurements()/remove_measurements()andhas_barriers()/remove_barriers(); a box left empty by a removal is dropped, since pyqasm rejects a box with no statements. Two consequences of the same blind spot are fixed alongside: a qubit operated on only inside anifblock no longer counts as idle, andremove_idle_qubits()no longer raisesAssertionErroron a program that mixes physical qubits with declared registers. (#345) - Fixed
unroll(consolidate_qubits=True)raisingAttributeError: 'str' object has no attribute 'name'for any gate applied to a physical qubit, e.g.h $1;. Consolidation assumed every gate operand was anIndexedIdentifier, but a physical qubit survives unrolling asIdentifier("$1"). Physical qubits are absolute hardware indices belonging to no declared register, so they are now left as written — matching howmeasure,resetandbarrieralready treat them. (#344) - Fixed
unroll()andrebase()emitting statements that share operand AST nodes: gate decompositions passed the sameIndexedIdentifierobjects into every statement they emitted, so transformations that rewrite qubit indices in place mutated a shared node once per referencing statement. This crashedreverse_qubit_order()(KeyError: -1) andremove_idle_qubits()(KeyError, #331) on any decomposed gate (e.g.crz) whenever the remap was not the identity. Statement constructors inmaps/gates.pyandDecomposernow copy their qubit operands so every emitted statement owns its nodes. (#333) - Fixed statements that OpenQASM 2 cannot condition being accepted as the body of a classical conditional. The QASM 2 grammar admits only a
<qop>— a gate application, a measurement or a reset — as the body of anif.barrieris a separate production, anddelay/boxhave no QASM 2 syntax at all, yetif(m==1) barrier q;,if(m==1) delay[10ns] q;andif(m==1) box {...}all validated and were emitted unchanged, producing output that QASM 2 parsers reject. Conditional bodies are now filtered against the<qop>whitelist at any nesting depth, raising aValidationErrorthat names the offending keyword and its source span. (#339) - Fixed
remove_idle_qubits(in_place=False)updating the qubit count on the wrong module: the original module'snum_qubitswas decremented while the returned copy kept the stale pre-removal count. The copy's AST was already correct; only the counters were swapped. (#336) - Fixed
remove_idle_qubits()raisingKeyErrorwhen the unrolled AST contains operand nodes shared across multiple statements (e.g. thecrzdecomposition) and an idle lower-indexed qubit shifts the register indices._remap_qubitsnow remaps each operand node exactly once instead of once per statement that references it. (#332) - Fixed
boxduration validation summingdelaydurations across all qubits instead of tracking each qubit's timeline. Delays on disjoint qubits run in parallel, sobox[300ns] { delay[200ns] q[0]; delay[200ns] q[1]; }was rejected while the identical schedule written as a broadcast delay (delay[200ns] q;) was accepted. Delays are now accumulated per qubit and the box is validated against the busiest single timeline; the error message names the offending qubit. Nested boxes now also contribute their declared duration to the enclosing box's timelines (previously the accumulator was reset when an inner box closed, dropping all inner delay accounting). (#330)
Dependencies
- Updated
docutilsrequirement from<0.23to<0.24. (#319) - Bumped
codecov/codecov-actionfrom 6.0.0 to 7.0.0. (#321) - Bumped
actions/checkoutfrom 6 to 7. (#323) - Updated
pillowrequirement from<12.3.0to<12.4.0. (#324) - Bumped
actions/setup-pythonfrom 6 to 7. (#328)
New Contributors
- @SAY-5 made their first contribution in #332
- @sathichaitanyasaidurga made their first contribution in #349
- @micpap25 made their first contribution in #346
Full Changelog: v1.0.4...v1.1.0