<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pratul Deshpande</title><description>Technical writing on quantum computing, quantum control systems and programming languages.</description><link>https://blog.pratuldeshpande.com/</link><atom:link href="https://blog.pratuldeshpande.com/rss.xml" rel="self" type="application/rss+xml"/><item><title>Same &apos;if&apos;, different timing</title><link>https://blog.pratuldeshpande.com/posts/same-if-different-timing/</link><guid isPermaLink="true">https://blog.pratuldeshpande.com/posts/same-if-different-timing/</guid><description>One measurement-conditioned if, four quantum control stacks, four different answers about time. Why that matters for idle qubits, error correction and portability.</description><pubDate>Sun, 15 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;aside class=&quot;reader-note&quot; aria-label=&quot;How to read this post&quot;&gt;
&lt;p&gt;&lt;strong&gt;How to read this post.&lt;/strong&gt; Every factual claim below links to the document it comes from. I tag the rows of the comparison table as &lt;strong&gt;Observed&lt;/strong&gt; (stated in a vendor’s or standard’s own documentation) or &lt;strong&gt;Argument&lt;/strong&gt; (my reasoning from those facts). Where I looked and found nothing, I say so. Views are my own. If I got a vendor’s behaviour wrong, tell me and I will correct the post with a dated note.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;Here is a small quantum program. Measure a qubit. If the result is 1, apply a correction to a second qubit. Then carry on.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark-high-contrast&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#0a0c10;color:#24292e;--shiki-dark:#f0f3f6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;qasm&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#FF9492&quot;&gt;OPENQASM&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt; 3.0&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#FF9492&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#ADDCFF&quot;&gt; &quot;stdgates.inc&quot;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#FF9492&quot;&gt;qubit&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;] q;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#FF9492&quot;&gt;bit&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt; c;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;h&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt; q[&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;cx&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt; q[&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;], q[&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;];      &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#BDC4CC&quot;&gt;// q[0] and q[1] are now entangled&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;c = &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#FF9492&quot;&gt;measure&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt; q[&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;];   &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#BDC4CC&quot;&gt;// read q[0] in the middle of the program&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#FF9492&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt; (c == &lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;    x&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt; q[&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;];         &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#BDC4CC&quot;&gt;// correction: two gates long&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;    z&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt; q[&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#FF9492&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#BDC4CC&quot;&gt;    // nothing to do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;h&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt; q[&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#91CBFF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#F0F3F6&quot;&gt;];             &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#BDC4CC&quot;&gt;// q[2] has been waiting this whole time&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every programmer can read this. Now ask a question the code does not answer: &lt;strong&gt;at what time does &lt;code&gt;h q[2]&lt;/code&gt; run?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On a laptop you would never ask. A branch takes a few nanoseconds longer or shorter, and nothing cares. On a quantum computer the answer decides whether the waiting qubit, &lt;code&gt;q[2]&lt;/code&gt;, is still holding its information when you get to it. And when I read the documentation of the stacks that run programs like this, I found that they answer the question differently. Some make both arms of the &lt;code&gt;if&lt;/code&gt; take the same time. Some synchronize and accept a small gap. One hands the clock to the programmer and raises an error if you get it wrong. One avoids most of the question by restricting what you can write.&lt;/p&gt;
&lt;p&gt;This post explains why time is part of a quantum program’s meaning, what each of those answers looks like, and why I think the missing piece is a written statement of what each layer of the stack promises the next. If you have never written a quantum program, start at the next section. If you have, skip to &lt;a href=&quot;https://blog.pratuldeshpande.com/posts/same-if-different-timing/#four-answers-to-one-question&quot;&gt;Four answers to one question&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;background-qubits-forget-so-time-costs-something&quot;&gt;Background: qubits forget, so time costs something&lt;/h2&gt;
&lt;p&gt;A classical bit sitting in memory stays a 0 or a 1 until something changes it. A qubit does not sit still. It holds a delicate superposition, and its environment keeps nudging it: stray electromagnetic fields, tiny fluctuations in the chip’s materials, heat. Over time the stored state drifts and blurs. Physicists call this &lt;strong&gt;decoherence&lt;/strong&gt;, and on today’s superconducting chips the useful lifetime of a qubit’s state is on the order of tens to hundreds of microseconds.&lt;/p&gt;
&lt;p&gt;That turns a quantum program into a race. Every operation, including doing nothing, uses up some of that lifetime. A qubit that sits idle for a microsecond is not “paused”. It is decaying.&lt;/p&gt;
&lt;p&gt;This is why quantum compilers do something classical compilers never do: they &lt;strong&gt;schedule&lt;/strong&gt; the program in physical time. Every gate gets a start time and a duration, measured in nanoseconds, for every qubit. The schedule is not a performance detail. It determines how much error the program accumulates.&lt;/p&gt;
&lt;h3 id=&quot;dynamic-circuits-programs-that-react&quot;&gt;Dynamic circuits: programs that react&lt;/h3&gt;
&lt;p&gt;For most of quantum computing’s history, programs were fixed lists of gates, with all measurements at the very end. A &lt;strong&gt;dynamic circuit&lt;/strong&gt; breaks that rule. It measures some qubits in the middle of the run, sends the result to a classical controller, and lets that controller decide what to do next while the other qubits are still alive. The step from measurement to decision to action is called &lt;strong&gt;feedforward&lt;/strong&gt;.&lt;/p&gt;
&lt;details class=&quot;figure-zoom&quot;&gt;&lt;summary&gt;&lt;img alt=&quot;Sequence diagram: qubit q[0] sends a measurement pulse to the readout electronics, which pass the result c = 0 or 1 to the classical controller. The controller decides which branch to run and plays a correction pulse on target qubit q[1] only if c == 1, while every other qubit idles.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1167&quot; height=&quot;483&quot; src=&quot;https://blog.pratuldeshpande.com/_astro/feedforward-sequence.BBICQJhK_25yAjs.svg&quot;&gt;&lt;/summary&gt;&lt;/details&gt;
&lt;p&gt;Dynamic circuits are not exotic. Quantum teleportation needs one: you measure, then correct the receiving qubit depending on the outcome. Resetting a qubit mid-program is measure-then-flip-if-needed. And quantum error correction, the thing every large machine will depend on, is a loop of measuring check qubits, decoding what went wrong, and applying corrections, forever.&lt;/p&gt;
&lt;h3 id=&quot;how-long-is-a-step&quot;&gt;How long is “a step”?&lt;/h3&gt;
&lt;p&gt;It helps to put real numbers on this. IBM’s documentation shows the properties of one qubit on a Heron r2 processor: a two-qubit CZ gate takes 68 nanoseconds and a measurement takes 2,684 nanoseconds (&lt;a href=&quot;https://quantum.cloud.ibm.com/docs/en/guides/qpu-information&quot;&gt;IBM, View backend details&lt;/a&gt;). IBM’s November 2025 announcement put its feedforward latency at about 600 nanoseconds (&lt;a href=&quot;https://www.ibm.com/quantum/blog/utility-scale-dynamic-circuits&quot;&gt;IBM Quantum blog&lt;/a&gt;). These numbers vary by qubit and by calibration day, but the proportions are what matter.&lt;/p&gt;
&lt;details class=&quot;figure-zoom&quot;&gt;&lt;summary&gt;&lt;img alt=&quot;Bar chart of a CZ gate (68 ns), feedforward (about 600 ns), measurement (2,684 ns), and measure-plus-decide (about 3,284 ns)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;860&quot; height=&quot;330&quot; src=&quot;https://blog.pratuldeshpande.com/_astro/fig-timescales.CGgaq_xz_2c8CMs.svg&quot;&gt;&lt;/summary&gt;&lt;/details&gt;
&lt;p&gt;Measure plus decide comes to about 3.3 microseconds, the time it takes to run roughly 48 two-qubit gates. IBM says this plainly in its own guide: mid-circuit measurements and classical operations “typically have longer execution time than two-qubit gates”, which can cancel out the benefit of a shorter circuit (&lt;a href=&quot;https://quantum.cloud.ibm.com/docs/en/guides/execute-dynamic-circuits&quot;&gt;IBM, Execute dynamic circuits&lt;/a&gt;). In my example program, &lt;code&gt;q[2]&lt;/code&gt; spends that whole stretch waiting.&lt;/p&gt;
&lt;h2 id=&quot;idle-time-is-not-wasted-time-if-you-know-how-long-it-is&quot;&gt;Idle time is not wasted time, if you know how long it is&lt;/h2&gt;
&lt;p&gt;There is a standard trick for protecting a waiting qubit. It is called &lt;strong&gt;dynamical decoupling&lt;/strong&gt;, and the idea is older than quantum computing. If a qubit is slowly drifting in one direction, you flip it over halfway through the wait. Now the same drift pushes it back the other way, and by the end of the wait the two halves roughly cancel. Do this with a few carefully spaced flips and much of the slow noise averages out. The flips add up to “do nothing”, so they don’t change what the program computes.&lt;/p&gt;
&lt;p&gt;The catch is in the phrase &lt;em&gt;carefully spaced&lt;/em&gt;. The flips have to be placed relative to the start and end of the idle window. Qiskit’s standard pass for this, &lt;code&gt;PadDynamicalDecoupling&lt;/code&gt;, says so directly: it “works on a scheduled, physical circuit”, scans for idle periods (the explicit delays the scheduler inserted), and fills them with decoupling pulses (&lt;a href=&quot;https://quantum.cloud.ibm.com/docs/en/api/qiskit/qiskit.transpiler.passes.PadDynamicalDecoupling&quot;&gt;Qiskit API docs&lt;/a&gt;). IBM’s own announcement ties the two together: because mid-circuit measurements are long, qubits that aren’t measured “are left to sit idle and decohere”, and a more flexible way of specifying durations is what lets you run accurate decoupling during those waits (&lt;a href=&quot;https://www.ibm.com/quantum/blog/utility-scale-dynamic-circuits&quot;&gt;IBM Quantum blog&lt;/a&gt;).&lt;/p&gt;
&lt;details class=&quot;figure-zoom&quot;&gt;&lt;summary&gt;&lt;img alt=&quot;Two timelines. Top: a known idle window with four evenly spaced echo pulses. Bottom: a window whose end depends on a branch, so the planned spacing no longer fits.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;900&quot; height=&quot;300&quot; src=&quot;https://blog.pratuldeshpande.com/_astro/fig-dd-window.DVzm_ec6_ZU27E3.svg&quot;&gt;&lt;/summary&gt;&lt;/details&gt;
&lt;p&gt;So the compiler needs to know how long each idle window is. For a fixed list of gates, it does. Now put an &lt;code&gt;if&lt;/code&gt; in the middle.&lt;/p&gt;
&lt;h2 id=&quot;the-branch-problem&quot;&gt;The branch problem&lt;/h2&gt;
&lt;p&gt;Go back to the example. The &lt;code&gt;if&lt;/code&gt; arm has two gates. The &lt;code&gt;else&lt;/code&gt; arm has none. Those two paths take different amounts of time, and which one runs is decided by a measurement result that nobody knows until the program is running.&lt;/p&gt;
&lt;p&gt;That leaves the next instruction, &lt;code&gt;h q[2]&lt;/code&gt;, with two possible start times. Or more, if the feedforward latency itself varies from shot to shot. The compiler, which wanted to fill &lt;code&gt;q[2]&lt;/code&gt;’s idle window with evenly spaced decoupling pulses, no longer knows where the window ends.&lt;/p&gt;
&lt;p&gt;Something in the stack has to settle this. Either the language says what an &lt;code&gt;if&lt;/code&gt; costs in time, or the compiler decides, or the control hardware decides at run time. To see who does, I went through the specification of the language most of these programs are written in, and then the documentation of four control systems.&lt;/p&gt;
&lt;h3 id=&quot;what-the-language-says&quot;&gt;What the language says&lt;/h3&gt;
&lt;p&gt;The example above is OpenQASM 3, the most widely supported language for this kind of program. It has a rich vocabulary for time: durations, explicit &lt;code&gt;delay&lt;/code&gt;s, flexible &lt;code&gt;stretch&lt;/code&gt; durations that the compiler solves for, and a &lt;code&gt;box&lt;/code&gt; that groups instructions into a block. Its circuit-timing chapter is clear that this is a compile-time system: stretch variables “are resolved at compile time”, and all arithmetic on durations happens at compile time (&lt;a href=&quot;https://openqasm.com/language/delays.html&quot;&gt;OpenQASM, Circuit timing&lt;/a&gt;). The same chapter offers &lt;code&gt;box&lt;/code&gt; for code whose exact duration “is runtime dependent”, so that you can impose a duration on it for scheduling purposes.&lt;/p&gt;
&lt;p&gt;The one firm rule about branches and time that I found is in the pulse-level chapter, which covers hand-written calibrations (&lt;code&gt;defcal&lt;/code&gt; blocks). There, each branch of the control flow must have “definite and equivalent duration resolvable at compile time”. The spec’s own example is a reset: measure, and if the result is 1 flip the qubit, otherwise “delay for an equivalent amount of time” (&lt;a href=&quot;https://openqasm.com/language/pulses.html&quot;&gt;OpenQASM, Pulse-level descriptions&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I did not find a matching rule for an ordinary circuit-level &lt;code&gt;if&lt;/code&gt; like the one in my example. If I missed it, please point me to it. As far as I can tell, the question is left to whoever runs the program.&lt;/p&gt;
&lt;details class=&quot;figure-zoom&quot;&gt;&lt;summary&gt;&lt;img alt=&quot;Flow diagram: the program (OpenQASM 3 or Qiskit) passes to the compiler and scheduler, which places every gate in time, then to the control system (FPGAs, sequencers), which sends pulses to the qubits; the qubits send the measurement result back to the control system. The program-to-compiler arrow is labelled &amp;quot;if (c == 1) { ... }, how long?&amp;quot; and the compiler-to-control arrow &amp;quot;timed instructions, how long?&amp;quot;.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1247&quot; height=&quot;114&quot; src=&quot;https://blog.pratuldeshpande.com/_astro/stack-handoffs.jy1NbRhH_L0Bp7.svg&quot;&gt;&lt;/summary&gt;&lt;/details&gt;
&lt;p&gt;Every arrow in that diagram is a handoff between two pieces of software or hardware, usually built by different teams. Each layer is careful about time internally. What I couldn’t find is a written statement, at each arrow, of what the layer below guarantees about the duration of a branch.&lt;/p&gt;
&lt;h2 id=&quot;four-answers-to-one-question&quot;&gt;Four answers to one question&lt;/h2&gt;
&lt;p&gt;Here is what the control systems’ own documentation says. I have kept to what each document states, and I flag what it doesn’t.&lt;/p&gt;
&lt;details class=&quot;figure-zoom&quot;&gt;&lt;summary&gt;&lt;img alt=&quot;Four timelines for the same program: padding, synchronizing with a gap, an exposed timeline that can underflow, and restricting branches&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;900&quot; height=&quot;640&quot; src=&quot;https://blog.pratuldeshpande.com/_astro/fig-four-answers.d_fFvI_L_1PO8Wh.svg&quot;&gt;&lt;/summary&gt;&lt;/details&gt;
&lt;h3 id=&quot;1-pad-both-arms-to-the-same-length&quot;&gt;1. Pad both arms to the same length&lt;/h3&gt;
&lt;p&gt;Qblox’s Q1 sequencers handle conditional playback with an instruction called &lt;code&gt;set_cond&lt;/code&gt;. When the next real-time instruction comes up, the hardware checks the condition. If it holds, the instruction runs. If not, the hardware waits for a fixed time, the &lt;code&gt;else_duration&lt;/code&gt;, that the programmer supplied (&lt;a href=&quot;https://docs.qblox.com/en/main/products/qblox_instruments/q1/index.html&quot;&gt;Qblox Q1ASM guide&lt;/a&gt;). The effect is that the timeline advances by the same amount whichever way the condition goes. The &lt;code&gt;defcal&lt;/code&gt; rule in OpenQASM above is the same idea, written into a language spec.&lt;/p&gt;
&lt;p&gt;The appeal is obvious: everything after the branch happens at a time the compiler knows. The price is also obvious: the short arm idles for as long as the long arm runs, every single time.&lt;/p&gt;
&lt;h3 id=&quot;2-synchronize-and-accept-a-gap&quot;&gt;2. Synchronize, and accept a gap&lt;/h3&gt;
&lt;p&gt;Quantum Machines’ QUA language takes a different route. Its timing guide says there is an implicit &lt;code&gt;align&lt;/code&gt; whenever several elements take part in a flow-control branch, whether an &lt;code&gt;if&lt;/code&gt;, a loop or a &lt;code&gt;switch&lt;/code&gt;, and that they are “aligned in the beginning of the flow control branch” (&lt;a href=&quot;https://docs.quantum-machines.co/latest/docs/Guides/timing_in_qua/&quot;&gt;Quantum Machines, Timing in QUA&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;How costly that align is depends on whether the compiler can predict it. If every duration is known at compile time, the compiler replaces the hardware synchronization with precomputed waits and no gaps appear. But the guide states that any dependence on a runtime value, including “a branch condition”, is treated as non-deterministic. In that case the processing cores signal each other at run time, which takes several clock cycles and leaves gaps in the pulse sequence. The gap is smallest when the channels are on the same module and longest across separate chassis. QUA also lets you mark a block as &lt;code&gt;strict_timing_&lt;/code&gt;, in which case any gap raises an error instead.&lt;/p&gt;
&lt;p&gt;So here the branch is synchronized at its start, and the small, hardware-dependent gap is part of the deal.&lt;/p&gt;
&lt;h3 id=&quot;3-hand-the-timeline-to-the-programmer&quot;&gt;3. Hand the timeline to the programmer&lt;/h3&gt;
&lt;p&gt;ARTIQ, the open-source control system used in many trapped-ion labs, makes time explicit in the program itself. A kernel keeps a timeline cursor, &lt;code&gt;now_mu&lt;/code&gt;, which the programmer advances with calls like &lt;code&gt;delay()&lt;/code&gt;. Events are scheduled at the cursor’s position. Trying to schedule an event at a time that has already passed raises an &lt;code&gt;RTIOUnderflow&lt;/code&gt; exception (&lt;a href=&quot;https://m-labs.hk/artiq/manual/rtio.html&quot;&gt;ARTIQ manual, Real-time I/O concepts&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;That design gives the programmer full control and full responsibility. If one arm of a branch needs more classical processing time than you budgeted, the next event lands in the past and you get an exception you can catch. The same manual also describes errors that behave differently. Sequence errors, collisions and busy errors “are not raised as exceptions and cannot be caught”. The offending event is discarded, the experiment keeps running, and the problem is reported in the core log. A timing mistake in a rarely taken arm can therefore remove a pulse from the run, and you learn about it from the log.&lt;/p&gt;
&lt;h3 id=&quot;4-allow-only-simple-branches&quot;&gt;4. Allow only simple branches&lt;/h3&gt;
&lt;p&gt;IBM’s cloud machines take the most conservative route I found. Of the control-flow structures OpenQASM 3 defines, IBM currently supports only the conditional &lt;code&gt;if&lt;/code&gt;. Nested conditionals are not allowed, measurements and resets inside a conditional are not supported, and &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt; and &lt;code&gt;switch&lt;/code&gt; are not supported (&lt;a href=&quot;https://quantum.cloud.ibm.com/docs/en/guides/execute-dynamic-circuits&quot;&gt;IBM, Execute dynamic circuits&lt;/a&gt;). IBM’s announcement adds that the new infrastructure can detect independent conditional operations and run them in parallel (&lt;a href=&quot;https://www.ibm.com/quantum/blog/utility-scale-dynamic-circuits&quot;&gt;IBM Quantum blog&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Restricting what you can write removes most of the hard cases. What I could not find in the pages I read is a statement of how two arms of unequal length are timed on IBM hardware. That is a gap in my reading, not a claim about IBM’s behaviour, and I’d be glad to be pointed to the right page.&lt;/p&gt;
&lt;h3 id=&quot;side-by-side&quot;&gt;Side by side&lt;/h3&gt;
&lt;div class=&quot;table-scroll&quot; role=&quot;region&quot; tabindex=&quot;0&quot; aria-label=&quot;Table&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stack&lt;/th&gt;
&lt;th&gt;What its documentation says about a branch&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenQASM 3, &lt;code&gt;defcal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every branch must have definite and equal duration, known at compile time (&lt;a href=&quot;https://openqasm.com/language/pulses.html&quot;&gt;spec&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Pad&lt;/td&gt;
&lt;td&gt;&lt;span class=&quot;status&quot; data-status=&quot;observed&quot;&gt;Observed&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenQASM 3, circuit-level &lt;code&gt;if&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No rule found in the circuit-timing chapter (&lt;a href=&quot;https://openqasm.com/language/delays.html&quot;&gt;spec&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Unspecified&lt;/td&gt;
&lt;td&gt;&lt;span class=&quot;status&quot; data-status=&quot;observed&quot;&gt;Observed&lt;/span&gt; (absence)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qblox Q1 (&lt;code&gt;set_cond&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;False condition waits &lt;code&gt;else_duration&lt;/code&gt; ns (&lt;a href=&quot;https://docs.qblox.com/en/main/products/qblox_instruments/q1/index.html&quot;&gt;docs&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Pad&lt;/td&gt;
&lt;td&gt;&lt;span class=&quot;status&quot; data-status=&quot;observed&quot;&gt;Observed&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quantum Machines QUA&lt;/td&gt;
&lt;td&gt;Implicit align at branch start; runtime branch conditions are non-deterministic and leave gaps of a few cycles (&lt;a href=&quot;https://docs.quantum-machines.co/latest/docs/Guides/timing_in_qua/&quot;&gt;docs&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Synchronize with gap&lt;/td&gt;
&lt;td&gt;&lt;span class=&quot;status&quot; data-status=&quot;observed&quot;&gt;Observed&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARTIQ&lt;/td&gt;
&lt;td&gt;Explicit cursor; past timestamps raise &lt;code&gt;RTIOUnderflow&lt;/code&gt;; some other errors drop the event and log it (&lt;a href=&quot;https://m-labs.hk/artiq/manual/rtio.html&quot;&gt;manual&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Exposed timeline&lt;/td&gt;
&lt;td&gt;&lt;span class=&quot;status&quot; data-status=&quot;observed&quot;&gt;Observed&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IBM Quantum&lt;/td&gt;
&lt;td&gt;Only un-nested &lt;code&gt;if&lt;/code&gt;; no measure or reset inside; feedforward about 600 ns (&lt;a href=&quot;https://quantum.cloud.ibm.com/docs/en/guides/execute-dynamic-circuits&quot;&gt;guide&lt;/a&gt;, &lt;a href=&quot;https://www.ibm.com/quantum/blog/utility-scale-dynamic-circuits&quot;&gt;blog&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Restrict&lt;/td&gt;
&lt;td&gt;&lt;span class=&quot;status&quot; data-status=&quot;observed&quot;&gt;Observed&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IBM Quantum, unequal arms&lt;/td&gt;
&lt;td&gt;Not stated in the pages I read&lt;/td&gt;
&lt;td&gt;Unknown&lt;/td&gt;
&lt;td&gt;&lt;span class=&quot;status&quot; data-status=&quot;not-verified&quot;&gt;Not verified&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All of the above&lt;/td&gt;
&lt;td&gt;The same source-level &lt;code&gt;if&lt;/code&gt; gets a different timing contract on each stack&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;span class=&quot;status&quot; data-status=&quot;argument&quot;&gt;Argument&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;None of these designs is wrong. Each makes sense for the hardware and users it was built for. The issue is that they are different, and nothing in the program says which one it expects.&lt;/p&gt;
&lt;h2 id=&quot;why-this-matters&quot;&gt;Why this matters&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Portability.&lt;/strong&gt; The same &lt;code&gt;if&lt;/code&gt; means different things in time depending on where it runs. A program whose correctness depends on a waiting qubit being protected for a known window carries that assumption silently. Move it to a different stack and the assumption changes without any line of code changing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Idle-time protection.&lt;/strong&gt; As above, decoupling sequences are placed by a compiler that needs to know window lengths. A branch makes the window depend on a measurement outcome and on feedforward latency. Padding gives the compiler a known window at the cost of extra idling. Synchronizing gives a shorter wait with a small unknown gap. The program can’t currently say which it wants.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Error correction raises the stakes.&lt;/strong&gt; In a fault-tolerant machine, reacting to measurements on time stops being a fidelity concern and becomes part of correctness. Gidney and Ekerå’s well-known estimate for factoring a 2048-bit RSA number with 20 million noisy qubits assumes a surface code cycle of 1 microsecond and a &lt;strong&gt;reaction time of 10 microseconds&lt;/strong&gt; (&lt;a href=&quot;https://arxiv.org/abs/1905.09749&quot;&gt;arXiv:1905.09749&lt;/a&gt;). For comparison, Google’s 2024 below-threshold experiment decoded in real time with an &lt;strong&gt;average decoder latency of 63 microseconds&lt;/strong&gt; at distance 5, against a 1.1-microsecond cycle (&lt;a href=&quot;https://arxiv.org/abs/2408.13687&quot;&gt;arXiv:2408.13687&lt;/a&gt;). Decoder latency and reaction time are not the same quantity, so this is not a like-for-like comparison. It does show that the time budgets are tight, and that timing will be stated as a requirement rather than measured after the fact.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The rare branch is the one you never test.&lt;/strong&gt; A correction arm that runs once in a hundred shots gets very little real-world exercise, and the one that runs once in a million gets almost none. If its timing is wrong, testing is a poor way to find out. This is the classic argument for checking timing by analysis instead of by observation.&lt;/p&gt;
&lt;h2 id=&quot;other-fields-learned-this-the-hard-way&quot;&gt;Other fields learned this the hard way&lt;/h2&gt;
&lt;p&gt;None of this is new outside quantum computing. Two well-documented failures show what happens when a timing assumption lives only in someone’s head.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Patriot battery at Dhahran, 1991.&lt;/strong&gt; On 25 February 1991, a Patriot missile battery in Dhahran, Saudi Arabia, failed to intercept an incoming Scud, which struck an Army barracks and killed 28 American soldiers. The US General Accounting Office (now the Government Accountability Office) traced it to software. The system’s registers were 24 bits long, and converting the clock’s integer time to a real number lost a little precision each time. The error grew with uptime. After 100 hours of continuous operation the time error was about 0.34 seconds, enough to shift the radar’s range gate by 687 metres, so the system looked in the wrong place. Alpha Battery had been running for over 100 consecutive hours (&lt;a href=&quot;https://www.gao.gov/products/imtec-92-26&quot;&gt;GAO report IMTEC-92-26&lt;/a&gt;; &lt;a href=&quot;https://cs.nyu.edu/exact/resource/mirror/patriot.htm&quot;&gt;full text mirror&lt;/a&gt;). The rule that mattered, roughly “this is only accurate for so many hours of uptime”, was not part of anything the software checked.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Boeing 787’s 248-day counter.&lt;/strong&gt; In 2015 the US Federal Aviation Administration issued an airworthiness directive for every 787. A software counter inside the generator control units would overflow after 248 days of continuous power and push the unit into failsafe mode. If all four main units had been powered up at the same moment, they would all fail together after 248 days, “resulting in a loss of all AC electrical power regardless of flight phase” (&lt;a href=&quot;https://www.federalregister.gov/documents/2015/05/01/2015-10066/airworthiness-directives-the-boeing-company-airplanes&quot;&gt;Federal Register, 1 May 2015&lt;/a&gt;). No flight test runs for 248 days. It is the rarely taken path from the previous section in its purest form.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What embedded systems did about it.&lt;/strong&gt; Real-time systems research has a long-standing answer: separate &lt;em&gt;when an operation is supposed to happen&lt;/em&gt; from &lt;em&gt;how long the hardware actually takes&lt;/em&gt;, and write the first one into the program. The Logical Execution Time idea, introduced with the Giotto language in 2000, gives each task a fixed logical time window. The implementation’s job is to meet that window, and if it does, the observable timing is the same on any machine (&lt;a href=&quot;https://www2.eecs.berkeley.edu/Pubs/TechRpts/2000/6436.html&quot;&gt;Giotto technical report&lt;/a&gt;; &lt;a href=&quot;https://www.researchgate.net/publication/221350878_The_Logical_Execution_Time_Paradigm&quot;&gt;LET paradigm overview&lt;/a&gt;). More recent work such as Lingua Franca makes the same split between logical and physical time, and states plainly which bounds the guarantee depends on, so that a violation is detectable rather than silent (&lt;a href=&quot;https://par.nsf.gov/servlets/purl/10554371&quot;&gt;Lohstroh et al., ACM TECS 2024&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A simpler picture.&lt;/strong&gt; Think of a railway line where two routes split at a junction and rejoin later. The timetable doesn’t describe the track. It promises when the train arrives at the rejoining station, and each route is either fast enough to meet that time or waits at a signal until it does. Everyone downstream plans around the promise. A quantum &lt;code&gt;if&lt;/code&gt; is a junction with no timetable: each control stack decides on its own how long the detour takes.&lt;/p&gt;
&lt;h2 id=&quot;what-is-missing&quot;&gt;What is missing&lt;/h2&gt;
&lt;p&gt;Put the pieces together. Each layer of a quantum stack is careful about time inside itself. The language specifies durations at compile time. The compiler schedules every gate. The control hardware is deterministic down to the clock cycle once an instruction is committed. Where they meet, at the branch, nobody writes down what one layer promises the next.&lt;/p&gt;
&lt;p&gt;What would such a statement contain? I think at least four things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how long a branch may take, as a bound rather than a hope;&lt;/li&gt;
&lt;li&gt;whether the arms are padded, synchronized, or left to the programmer;&lt;/li&gt;
&lt;li&gt;how long a measurement result takes to become usable, and where that number comes from (a certified worst case, a measured maximum, or a vendor’s typical figure);&lt;/li&gt;
&lt;li&gt;what happens when something is late: an error, a dropped event, or a silent delay.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that written down, a compiler could know whether the idle window it is about to fill with decoupling pulses really ends where it thinks it does. A program could say “this branch needs padding” and be told on the wrong hardware that it won’t get it. Error-correction designers could check a reaction-time requirement before running anything.&lt;/p&gt;
&lt;p&gt;I am working on how to state and check this. That will come later, and I’d rather the problem be understood before I propose an answer. For now, if you build control software, compilers or error-correction stacks, I’d like to hear two things: whether your system makes a timing promise about branches that I missed, and whether the four categories above match how you think about it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-i-do-not-claim&quot;&gt;What I do not claim&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I don’t claim any of these designs is a bug. Each is a deliberate trade-off.&lt;/li&gt;
&lt;li&gt;I don’t claim which choice is best for fidelity. That depends on hardware, noise and workload, and I have not measured it.&lt;/li&gt;
&lt;li&gt;I didn’t run code on any of these systems for this post. Everything above comes from public documentation, read in November 2026. Documentation changes, so check the linked pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;corrections&quot;&gt;Corrections&lt;/h3&gt;
&lt;p&gt;None yet. Corrections will be listed here with dates.&lt;/p&gt;
&lt;h3 id=&quot;sources&quot;&gt;Sources&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Language and compilers&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OpenQASM live specification, &lt;a href=&quot;https://openqasm.com/language/delays.html&quot;&gt;Circuit timing&lt;/a&gt; and &lt;a href=&quot;https://openqasm.com/language/pulses.html&quot;&gt;Pulse-level descriptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Qiskit API, &lt;a href=&quot;https://quantum.cloud.ibm.com/docs/en/api/qiskit/qiskit.transpiler.passes.PadDynamicalDecoupling&quot;&gt;PadDynamicalDecoupling&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Control systems and hardware&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Qblox, &lt;a href=&quot;https://docs.qblox.com/en/main/products/qblox_instruments/q1/index.html&quot;&gt;Q1ASM user guide&lt;/a&gt; (docs version 2026.10)&lt;/li&gt;
&lt;li&gt;Quantum Machines, &lt;a href=&quot;https://docs.quantum-machines.co/latest/docs/Guides/timing_in_qua/&quot;&gt;Timing in QUA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;M-Labs, &lt;a href=&quot;https://m-labs.hk/artiq/manual/rtio.html&quot;&gt;ARTIQ Real-Time I/O concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;IBM Quantum, &lt;a href=&quot;https://quantum.cloud.ibm.com/docs/en/guides/execute-dynamic-circuits&quot;&gt;Execute dynamic circuits&lt;/a&gt;, &lt;a href=&quot;https://quantum.cloud.ibm.com/docs/en/guides/qpu-information&quot;&gt;View backend details&lt;/a&gt;, and &lt;a href=&quot;https://www.ibm.com/quantum/blog/utility-scale-dynamic-circuits&quot;&gt;Utility-scale dynamic circuits now available for all users&lt;/a&gt; (Nov 2025)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Fault tolerance&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gidney and Ekerå, &lt;a href=&quot;https://arxiv.org/abs/1905.09749&quot;&gt;How to factor 2048 bit RSA integers in 8 hours using 20 million noisy qubits&lt;/a&gt;, Quantum 5, 433 (2021)&lt;/li&gt;
&lt;li&gt;Google Quantum AI, &lt;a href=&quot;https://arxiv.org/abs/2408.13687&quot;&gt;Quantum error correction below the surface code threshold&lt;/a&gt;, arXiv 2024, published in Nature&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Other fields&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;US GAO, &lt;a href=&quot;https://www.gao.gov/products/imtec-92-26&quot;&gt;Patriot Missile Defense: Software Problem Led to System Failure at Dhahran, Saudi Arabia&lt;/a&gt;, IMTEC-92-26 (1992)&lt;/li&gt;
&lt;li&gt;US FAA, &lt;a href=&quot;https://www.federalregister.gov/documents/2015/05/01/2015-10066/airworthiness-directives-the-boeing-company-airplanes&quot;&gt;Airworthiness Directives; The Boeing Company Airplanes&lt;/a&gt;, Federal Register, 1 May 2015&lt;/li&gt;
&lt;li&gt;Henzinger, Horowitz and Kirsch, &lt;a href=&quot;https://www2.eecs.berkeley.edu/Pubs/TechRpts/2000/6436.html&quot;&gt;Giotto: a time-triggered language for embedded programming&lt;/a&gt; (2000); Kirsch and Sokolova, &lt;a href=&quot;https://www.researchgate.net/publication/221350878_The_Logical_Execution_Time_Paradigm&quot;&gt;The Logical Execution Time paradigm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Lohstroh et al., &lt;a href=&quot;https://par.nsf.gov/servlets/purl/10554371&quot;&gt;Deterministic coordination across multiple timelines&lt;/a&gt;, ACM TECS (2024)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>quantum computing</category><category>dynamic circuits</category><category>OpenQASM</category><category>real-time systems</category><category>control systems</category></item></channel></rss>