2.7. Conditional Execution¶
The qctoolkit is desinged to support conditional execution of pulse (segments). This allows pulse designers to play back different waveforms depending on, e.g., environment data or state measurements of the quantum dot. Conditional execution may be implemented via trigger-based jumps to instructions directly on the playback device or emulated in software by choosing which pulse to send to the hardware for playback, if the hardware itself does not support branching.
Since the decision whether a condition will be evaluated software- or hardware-based depends on the hardware setup of the experiment and not on the pulse template the qctoolkit relies on an indirection scheme for conditions which is similar to the handling of parameters, as you will see in the following.
qctoolkit offers two PulseTemplate
subclasses for conditional
execution: LoopPulseTemplate
and BranchPulseTemplate
(Note:
BranchPulseTemplate is currently pending implementation (`issue
22 <https://github.com/qutech/qc-toolkit/issues/22>`__)).
LoopPulseTemplate
takes an identifier for a condition and a
subtemplate which is repeated as long as the condition evaluates to
True
. Let’s assume that we want to construct a pulse that waits
until some initialization is completed using a TablePulseTemplate
representing a zero-pulse of length 5 and a LoopPulseTemplate
:
In [1]:
from qctoolkit.pulses import LoopPulseTemplate, TablePulseTemplate
wait_template = TablePulseTemplate()
wait_template.add_entry(5, 0)
loop_template = LoopPulseTemplate('initialization_in_progress', wait_template)
loop_template
is now configured to evaluate a condition called
‘initialization_in_progress’. How this condition is implemented needs
not to be specified to declare pulse templates.
We will now look into the actual implementation of conditions. The
abstract interface of conditions is the Condition
class. As
mentioned in the beginning, conditions can be evaluated software- and
hardware-based. The classes SoftwareCondition
and
HardwareCondition
represent this in the qctoolkit. Note that these
classes don’t do the actual evaluation but encapsulate it against the
Sequencer
and are used to generate correct sequences for both cases.
Instances of Condition
s are passed directly into the Sequencer
and mapped to the PulseTemplate
s via the identifier, similar to
parameters.
2.7.1. Software-Based Conditions¶
SoftwareCondition
takes a callback function which is called to
evaluate the condition (and thus must return a boolean value). During
the sequencing process, this function will be called. If the return
value is True
, the subtemplate will be included in the instruction
sequence and another evaluation will be made until the return value is
False
. The generated instruction sequence will thus contain a number
of repetitions of the subtemplate but no actual jumping instructions,
the loop is essentially unrolled. The callback function is passed an
integer value indicating the current iteration of the evaluation.
As an example, we could use this to repeat the wait_template
a fixed
number of times, say 5, as follows:
In [2]:
from qctoolkit.pulses import SoftwareCondition, Sequencer
constant_repeat_condition = SoftwareCondition(lambda x: x < 5)
conditions = {'initialization_in_progress': constant_repeat_condition}
s = Sequencer()
parameters = {}
s.push(loop_template, parameters, conditions)
instructions = s.build()
print(instructions)
[<qctoolkit.pulses.instructions.EXECInstruction object at 0x0000000007870C50>, <qctoolkit.pulses.instructions.EXECInstruction object at 0x0000000007870DA0>, <qctoolkit.pulses.instructions.EXECInstruction object at 0x0000000007870E10>, <qctoolkit.pulses.instructions.EXECInstruction object at 0x0000000007870E80>, <qctoolkit.pulses.instructions.EXECInstruction object at 0x0000000007870EF0>, <qctoolkit.pulses.instructions.STOPInstruction object at 0x0000000007870F28>]
We obtain an instruction sequence that repeats an execution instruction
(for the wait_template
waveform) five times. This is, of course, a
very simple example. The callback function passed into the
SoftwareCondition
instance will more likely evaluate some measured
data. Since this might not always be available in the sequencing pass,
the callback may return None
, which will interrupt the sequencing
process similar to the `requires_stop
method of the Parameter
class <05Parameters.ipynb>`__.
2.7.2. Hardware-Based Conditions¶
Since software-based evaluation of conditions is slow and might
interrupt the sequencing process, it might not always be applicable. If
supported by the hardware, hardware-based condition evaluation is
preferrable. For the sequencing process, this is represented by
instances of HardwareCondition
, which only take some identifier of
the hardware trigger. This must first be obtained from the hardware
currently not implemented and is embedded in the generated instruction
sequence which will contain jump instructions.
Assuming we have a hardware setup with a trigger that fires continuously until temperature threshold is reached and we want our pulse from above to repeat as long as the trigger fires. We can realize this as follows:
In [3]:
from qctoolkit.pulses import HardwareCondition, Trigger
# stub representation for the trigger which must be obtained from the hardware in a real use case
temperature_trigger = Trigger()
temperature_trigger_condition = HardwareCondition(temperature_trigger)
conditions = {'initialization_in_progress': temperature_trigger_condition}
s = Sequencer()
parameters = {}
s.push(loop_template, parameters, conditions)
instructions = s.build()
print(instructions)
[<qctoolkit.pulses.instructions.CJMPInstruction object at 0x000000000787F080>, <qctoolkit.pulses.instructions.STOPInstruction object at 0x000000000787F160>, <qctoolkit.pulses.instructions.EXECInstruction object at 0x000000000787F128>, <qctoolkit.pulses.instructions.GOTOInstruction object at 0x000000000787F1D0>]
As you can see in the output, the sequencing process now procudes instructions that perform conditional jumps and returns with goto instructions. The details are omitted here, suffice it to say that the trigger is embedded in the conditional jump instruction and this sequence will loop on the hardware as long as the trigger fires.