qat.middleend.passes.purr.transform module

class AcquireSanitisation

Bases: TransformPass

Sanitises the Acquire instruction: the first per pulse channel is split into an Acquire and a Delay, and other acquisitions have their delay removed.

Acquire instructions are defined by a “duration” for which they instruct the target to readout. They also contain a “delay” attribute, which instructions the acquisition to start after some given time. This pass separates acqusitions with a delay into two instructions for the first acquire that acts on the channel. For multiple acquisitions on a single channel, the delay is not needed for the following acquisitions, and is set to zero.

run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

Return type:

InstructionBuilder

class BatchedShots(target_data)

Bases: TransformPass

Determines how shots should be grouped when the total number exceeds that maximum allowed.

The target machine might have an allowed number of shots that can be executed by a single execution call. To execute a number of shots greater than this value, shots can be batched, with each batch executed by its own “execute” call on the target machine. For example, if the maximum number of shots for a target machine is 2000, but you required 4000 shots, then this could be done as [2000, 2000] shots.

Now consider the more complex scenario where 4001 shots are required. Clearly this can be done in three batches. While it is tempting to do this in batches of [2000, 2000, 1], for some target machines, specification of the number of shots can only be achieved at compilation (as opposed to runtime). Batching as described above would result in us needing to compile two separate programs. Instead, it makes more sense to batch the shots as three lots of 1334 shots, which gives a total of 4002 shots. The extra two shots can just be discarded at run time.

Warning

This pass makes certain assumptions about the IR, including that there is only at most one Repeat instruction that contributes to the number of readouts, and there is at most one readout per shot. It will change the number of shots in the Repeat instruction to the decided batch size, and store the total required shots in the IR, which is less than ideal. It would be nice to save this as an analysis result, but an unfortante side effect of the decoupled nature of the middle- and back-end is that the results manager might not necessarily be passed along.

Parameters:

target_data (TargetData) – Target-related information.

run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

class DeviceUpdateSanitisation

Bases: TransformPass

Duplicate DeviceUpdate instructions upsets the device injection mechanism, which causes corruption of the HW model.

In fact, a DeviceInjector is currently 1-1 associated with a DeviceUpdate instruction. When multiple DeviceUpdate instructions (sequentially) inject the same “target”, the first DeviceInjector assigns the (correct) value of the attribute (on the target) to the revert_value. At this point the HW model (or any other target kind) is dirty, and any subsequent DeviceInjector updater would surely assign the (wrong, usually a placeholder Variable) to its revert_value. This results in a corrupt HW model whereby reversion wouldn’t have the desired effect.

This pass is a (lazy) fix, which is to analyse when such cases happen and eliminate duplicate DeviceUpdate instructions that target THE SAME “attribute” on THE SAME “target” with THE SAME variable.

run(ir, res_mgr, met_mgr, *args, **kwargs)
class EndOfTaskResetSanitisation

Bases: TransformPass

Checks for a reset on each active qubit at the end of a task, and adds Reset operations if not found.

After each shot, it is expected that the qubit is returned to its ground state, ready for the next shot. This pass ensures this is the case by checking if the last “active” operation on an qubit is a Reset, and if not, adds a Reset to the end of the instruction list. By default, the Reset operation will have the drive channel of a qubit as its target. However, if the drive channel is not an active channel, a different active channel on the qubit will be used its place.

Reset instructions currently sit in a weird place. Their targets are drive channels to match the semantics of other quantum instructions (but are instantiated with a :class:Qubit). However, like measurements, resets are a qubit-level operation. You cannot reset the state of a pulse channel, the state is a property of the qubit! To avoid breaking changes, we’ll just deal with that for now, but hope to do better in the refactored instructions…

run(ir, res_mgr, *args, **kwargs)
Parameters:
Return type:

InstructionBuilder

class EvaluatePulses(ignored_shapes=None, acquire_ignored_shapes=None, eval_function=<function evaluate_shape>)

Bases: TransformPass

Evaluates the amplitudes of Pulse instructions, replacing them with a CustomPulse and accounting for the scale of the pulse channel.

Pulse instructions are defined by (often many) parameters. With the exception of specific shapes, they cannot be implemented directly on hardware. Instead, we evaluate the waveform at discrete times, and communicate these values to the target. This pass evaluates pulses early in the compilation pipeline.

The CustomPulse instructions will have ignore_channel_scale=True.

Parameters:
  • ignored_shapes (Optional[list[PulseShapeType]]) – A list of pulse shapes that are not evaluated to a custom pulse, defaults to [PulseShapeType.SQUARE].

  • acquire_ignored_shapes (Optional[list[PulseShapeType]]) – A list of pulse shapes that are not evaluated to a custom pulse for Acquire filters, defaults to [].

  • eval_function (callable) – Allows a pulse evaluation function to be injected, defaults to evaluate_shape().

evaluate_waveform(inst, ignored_shapes, pulse_lookup)

Evaluates the waveform for a Pulse or CustomPulse, accounting for the pulse channel scale.

Return type:

Pulse | CustomPulse

hash_pulse(pulse)

Hashs a pulse object.

Return type:

str

run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions as an instruction builder.

Return type:

InstructionBuilder

class FreqShiftSanitisation(model)

Bases: TransformPass

Looks for any active frequency shift pulse channels in the hardware model and adds square pulses for the duration.

Warning

This pass assumes the following, which is achieved via other passes:

  • Synchronize instructions have already been lowered to Delay instructions.

  • Durations are static.

Parameters:

model (QuantumHardwareModel) – The hardware model containing the frequency shift channels.

static add_freq_shift_to_ir(ir, freq_shift_channels)

Adds frequency shift instructions to the instruction builder.

Return type:

InstructionBuilder

get_freq_shift_channels()

Returns all active frequency shift pulse channels found in the hardware model.

Return type:

dict[PulseChannel, Qubit]

run(ir, res_mgr, *args, **kwargs)
Parameters:
Return type:

InstructionBuilder

class InactivePulseChannelSanitisation

Bases: TransformPass

Removes instructions that act on inactive pulse channels.

Many channels that aren’t actually needed to execute the program contain instructions, mainly Synchronize instructions and PhaseShift instructions which can happen when either of the instructions are applied to qubits. To simplify analysis and optimisations in later passes, it makes sense to filter these out to reduce the overall instruction amount.

Note

This pass requires results from the ActivePulseChannelAnalysis to be stored in the results manager.

run(ir, res_mgr, *args, **kwargs)
Parameters:
Return type:

InstructionBuilder

class InitialPhaseResetSanitisation

Bases: TransformPass

Checks if every active pulse channel has a phase reset in the beginning.

Warning

This pass implies that an ActiveChannelAnalysis is performed prior to this pass.

run(ir, res_mgr, *args, **kwargs)
class InstructionGranularitySanitisation(model, target_data)

Bases: TransformPass

Rounds the durations of quantum instructions so they are multiples of the clock cycle.

Only supports quantum instructions with static non-zero durations. Assumes that instructions with a non-zero duration only act on a single pulse channel. The santisiation is done for all instructions simultaneously using numpy for performance.

For CustomPulse instructions, the durations are rounded up by padding the pulse with zero amplitude at the end. For other relevant instructions, we round down: this is for compatibility with calibration files that are calibrated using legacy code. However, in the future we might consider changing this to round up for consistency.

The Pydantic version of the pass will require the (pydantic equivalent) pass ActivePulseChannelAnalysis to have run, with results saved to the results manager to extract pulse channel information.

Warning

This pass has the potential to invalidate the timings for sequences of instructions that are time-sensitive. For example, if a pulse has an invalid time, it will round it up to the nearest integer multiple. Furthemore, it will assume that Acquire instructions have no delay. This can be forced explicitly using the AcquireSanitisation pass.

Parameters:

target_data (TargetData) – Target-related information.

run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

Return type:

InstructionBuilder

sanitise_custom_pulses(instructions)

Sanitises the durations of CustomPulse instructions by padding the pulses with zero amplitudes.

sanitise_quantum_instructions(instructions)

Sanitises the durations quantum instructions with non-zero duration by rounding down to the nearest clock cycle.

class InstructionLengthSanitisation(target_data)

Bases: TransformPass

Checks if quantum instructions are too long and splits if necessary.

Parameters:

duration_limit – The maximum allowed clock cycles per instruction.

Warning

The pass will assume that the durations of instructions are sanitised to the granularity of the pulse channels. If instructions that do not meet the criteria are provided, it might produce incorrect instructions (i.e., instructions that are shorter than the clock cycle). This can be enforced using the InstructionGranularitySanitisation pass.

run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

class IntegratorAcquireSanitisation

Bases: TransformPass

Changes AcquireMode.INTEGRATOR acquisitions to AcquireMode.RAW.

The legacy echo/RTCS engines expect the acquisition mode to be either RAW or SCOPE. While the actual execution can process INTEGRATOR by treating it as RAW, they are typically santitised the runtime using EchoEngine.optimize(). If not done in the new pipelines, it will conflict with PostProcessingSantisiation, and return the wrong results. The new echo engine supports all acquisition modes, so this is not a problem here.

run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

class LegacyPhaseOptimisation

Bases: TransformPass

Iterates through the list of instructions and compresses contiguous PhaseShift instructions.

run(ir, res_mgr, met_mgr, *args, **kwargs)
Parameters:
  • ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

  • res_mgr (ResultManager) – The result manager to save the analysis results.

  • met_mgr (MetricsManager) – The metrics manager to store the number of instructions after optimisation.

class LoopCount

Bases: int

class LowerSyncsToDelays

Bases: TransformPass

Lowers Synchronize instructions to Delay instructions with static times.

Increments through the instruction list, keeping track of the cumulative duration. When Synchronize instructions are encountered, it is replaced with Delay instructions with timings calculated from the cumulative durations.

Warning

Any manipulations of the instruction set that will alter the timeline and occur after this pass could invalidate the intention of the Synchronize instruction.

run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

Return type:

InstructionBuilder

class MeasurePhaseResetSanitisation

Bases: TransformPass

Adds a phase reset before every measure pulse.

run(ir, *args, **kwargs)
class PhaseOptimisation

Bases: TransformPass

Iterates through the list of instructions and compresses contiguous PhaseShift instructions. This pass will change PhaseReset to PhaseSet instructions.

static merge_phase_instructions(target, phase1, phase2)
run(ir, res_mgr, met_mgr, *args, **kwargs)
Parameters:
  • ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

  • res_mgr (ResultManager) – The result manager to save the analysis results.

  • met_mgr (MetricsManager) – The metrics manager to store the number of instructions after optimisation.

class PostProcessingSanitisation

Bases: TransformPass

Checks that the PostProcessing instructions that follow an acquisition are suitable for the acquisition mode, and removes them if not.

Extracted from qat.purr.backends.live.LiveDeviceEngine.optimize().

run(ir, _, met_mgr, *args, **kwargs)
Parameters:
  • ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

  • met_mgr (MetricsManager) – The metrics manager to store the number of instructions after optimisation.

class QiskitInstructionsWrapper

Bases: TransformPass

Wraps the Qiskit builder in a wrapper to match the pipelines API.

A really silly pass needed to wrap the QiskitBuilder in an object that allows QiskitBuilderWrapper.instructions to be called, allowing the builder to be used in the the LegacyRuntime. This is needed because the qiskit engine has a different API to other purr engines, requiring the whole builder to be passed (as opposed to builder.instructions).

run(ir, *args, **kwargs)
Parameters:

ir (QiskitBuilder) – The Qiskit instructions

Return type:

QiskitBuilderWrapper

class RepeatSanitisation(model, target_data)

Bases: TransformPass

Adds repeat counts and repetition periods to Repeat instructions. If none is found, a repeat instruction is added.

Parameters:
run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

class RepeatTranslation(target_data)

Bases: TransformPass

Transform Repeat instructions so that they are replaced with: Variable, Assign, and Label instructions at the start, and Assign and Jump instructions at the end.

run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

Return type:

InstructionBuilder

class ResetsToDelays(target_data)

Bases: TransformPass

Transforms Reset operations to :class:`Delay`s.

Note that the delays do not necessarily agree with the granularity of the underlying target machine. This can be enforced using the InstructionGranularitySanitisation pass.

Parameters:

target_data (TargetData) – Target-related information.

run(ir, res_mgr, *args, **kwargs)
Parameters:
Return type:

InstructionBuilder

class ReturnSanitisation

Bases: TransformPass

Squashes all Return instructions into a single one. Adds a Return with all acquisitions if none is found.

run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

class ScopeSanitisation

Bases: TransformPass

Bubbles up all sweeps and repeats to the beginning of the list and adds delimiter instructions to the repeats and sweeps signifying the end of their scopes.

Warning

This pass is intended for use with legacy builders that do not use EndRepeat and EndSweep instructions. It will add a delimiter instruction for each Repeat and Sweep found in the IR.

run(ir, *args, **kwargs)
Parameters:

ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

class SquashDelaysOptimisation

Bases: TransformPass

Looks for consecutive Delay instructions on a pulse channel and squashes them into a single instruction.

Because Synchronize instructions across multiple pulse channels are used so frequently to ensure pulses play at the correct timings, it means we can have sequences of many delays. Reducing the number of delays will simplify timing analysis later in the compilation.

Delay instructions commute with phase related instructions, so the only instructions that separate delays in a meaningful way are: Pulse:, CustomPulse and Acquire instructions. We also need to be careful to not squash delays that contain a variable time.

run(ir, res_mgr, met_mgr, *args, **kwargs)
Parameters:
  • ir (InstructionBuilder) – The list of instructions stored in an InstructionBuilder.

  • res_mgr (ResultManager) – The result manager to save the analysis results.

  • met_mgr (MetricsManager) – The metrics manager to store the number of instructions after optimisation.

class SynchronizeTask

Bases: TransformPass

Synchronizes all active pulse channels in a task.

Adds a synchronize to the end of the instruction list for all active pulse channels, which is extracted from the ActivePulseChannelAnalysis pass. This is useful to do before resetting the qubits, as it ensures no qubit is reset while the task is still “active”, reducing the effects of cross-talk.

run(ir, res_mgr, *args, **kwargs)
Parameters:
Return type:

InstructionBuilder