A static DeviceUpdate is one that updates an attribute on a target with a value that's known at compile time.
For example DeviceUpdate(some_channel, "scale", 1) simply assigns 1 to the attribute scale on target some_channel. But how come people use this version instead of simply saying some_channel.scale = 1 ?
In fact, the legacy code processes DeviceUpdates with a structure called DeviceInjector not only to "inject" (i.e. assign at runtime) but to "revert" any previous assignment(s) at "some aftermath" (*). It's the injection + reversion guarantee that appeals to people more than the direct way. Therefore DeviceUpdate(some_channel, "scale", 1) is roughly equivalent to sth like:
Legacy code w/r to SweepIterator and DeviceInjectors worked fine because the (QAT IR) programs were simple basic blocks. In light of the recent efforts to bring control flow, analysis of these programs become (semantically) intractable. Therefore the problem being raised here is more on what static DeviceUpdate instructions mean globally for the QAT IR program and in awareness of control flow. Here are a few claims:
While they are perfectly valid instructions, people use static DeviceUpdates (only) to benefit from the assignment + reversion process described above (*).
Programmers writing QAT IR are likely unaware that the position of a static DeviceUpdate among other instructions is ultimately important.
Static DeviceUpdates can ultimately influence codegen. Pulse evaluation at compile time is one typical effect.
This PR only describes a peculiar case of DeviceUpdate in QAT IR and does not provide a definite solution. The workaround implemented in this PR assumes the claims laid out above. Here's what it contributes:
Adapt existing analyses to recognise static DeviceUpdates
Filter out any static DeviceUpdates prior to codegen
Process static DeviceUpdates globally and irrespective of any control flow considerations
A static
DeviceUpdate
is one that updates an attribute on a target with a value that's known at compile time.For example
DeviceUpdate(some_channel, "scale", 1)
simply assigns1
to the attributescale
on targetsome_channel
. But how come people use this version instead of simply sayingsome_channel.scale = 1
?In fact, the legacy code processes
DeviceUpdate
s with a structure calledDeviceInjector
not only to "inject" (i.e. assign at runtime) but to "revert" any previous assignment(s) at "some aftermath" (*). It's the injection + reversion guarantee that appeals to people more than the direct way. ThereforeDeviceUpdate(some_channel, "scale", 1)
is roughly equivalent to sth like:or via some mechanism based on Python contexts.
Legacy code w/r to
SweepIterator
andDeviceInjectors
worked fine because the (QAT IR) programs were simple basic blocks. In light of the recent efforts to bring control flow, analysis of these programs become (semantically) intractable. Therefore the problem being raised here is more on what staticDeviceUpdate
instructions mean globally for the QAT IR program and in awareness of control flow. Here are a few claims:DeviceUpdate
s (only) to benefit from the assignment + reversion process described above (*).DeviceUpdate
among other instructions is ultimately important.DeviceUpdate
s can ultimately influence codegen. Pulse evaluation at compile time is one typical effect.This PR only describes a peculiar case of
DeviceUpdate
in QAT IR and does not provide a definite solution. The workaround implemented in this PR assumes the claims laid out above. Here's what it contributes:DeviceUpdate
sDeviceUpdate
s prior to codegenDeviceUpdate
s globally and irrespective of any control flow considerations