Repository navigation
[BUG] dt duration unit incorrectly mapped to nanoseconds #301
Description
Activity
Hey @ryanhill1, I looked into this and tried to reproduce the bug and traced the root cause, but I think it differs slightly from the diagnosis in the description, so I wanted to confirm the intended fix before opening a PR.
from pyqasm import loads, dumps
qasm = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[1] q;
delay[100dt] q[0];
"""
m = loads(qasm); m.unroll()
print(dumps(m)) # delay[100.0ns] q[0]; <- should be 100.0dtRoot cause
The expression evaluator in src/pyqasm/expressions.py handles dt correctly, the DurationLiteral branch returns the raw value 100.0 and never touches TIME_UNITS_MAP for the dt case. The mislabeling happens afterwards, in the visitor, in _visit_delay_statement (src/pyqasm/visitor.py, ~L2830):
statement.duration = qasm3_ast.DurationLiteral(
duration_val,
unit=(
qasm3_ast.TimeUnit.dt
if self._module._device_cycle_time
else qasm3_ast.TimeUnit.ns # <- assigns ns even when source unit was dt
),
)The unit is chosen solely from whether device_cycle_time is set, ignoring the original statement.duration.unit. When no device_cycle_time is provided and the source unit is dt, it's incorrectly relabeled ns. SI units (us, ms, s) are fine here, because the evaluator genuinely converts them to ns first, only dt is affected.
The same pattern exists for the box duration in _visit_box_statement (~L2911), so box[200dt] { ... } also emits box[200.0ns].
Question on intended behaviour
The description lists two options. Which one should we prefer?
- Preserve dt in the unrolled AST when no
device_cycle_timeis set (value=100.0, unit=dt), or - Raise/warn that
dtcan't be converted to SI units without a sample rate
I have a working draft for option 1 that fixes both the delay and box paths with no regressions in the existing test suite.
Minimal change in _visit_delay_statement - preserve the source unit when it was dt:
source_is_dt = (
isinstance(_delay_time_var, qasm3_ast.DurationLiteral)
and _delay_time_var.unit == qasm3_ast.TimeUnit.dt
)
statement.duration = qasm3_ast.DurationLiteral(
duration_val,
unit=(
qasm3_ast.TimeUnit.dt
if self._module._device_cycle_time or source_is_dt
else qasm3_ast.TimeUnit.ns
),
)(Same adjustment applies to the box path.) With this, delay[100dt] -> delay[100.0dt], delay[2us] -> delay[2000.0ns] (unchanged), and the full test suite shows no new failures.
Let me know if I missed something, and if I can open a PR for this with the suitable fix.
Hi @ashmitjsg, thanks for your interest in this issue!
Your proposed fix for Option 1 sounds great. Feel free to open a PR
Thanks @ryanhill1. Opened #317 with the Option 1 fix (preserves dt for both delay and box). Please check it out, and let me know if there are any other changes required.
Description
The
dtduration unit in OpenQASM 3 is backend-dependent — it represents the duration of one waveform sample on the target hardware. It cannot be converted to SI units (ns, us, ms, s) without knowing the backend's sample rate.However, pyqasm currently treats
dtas equivalent tonsduring unrolling, producing incorrect duration values.Reproduction
Expected Behavior
delay[100dt]should either:dtas the unit in the unrolled AST (value=100.0, unit=dt), ordtcannot be converted to SI unitsActual Behavior
The duration is reported as
value=100.0, unit=ns— treating 1 dt = 1 ns, which is incorrect. TheTIME_UNITS_MAPinsrc/pyqasm/maps/expressions.pydoes not include adtentry, so the unit appears to fall through without proper handling.Reference
From the OpenQASM 3 spec: